- Home
- index
- sidebar
- Weblog
- life
- weblog
- searchresults
- About
- basic-custom.css
- backup
- permalink
- test
- kernel
- application
- opensolaris
- Login
UltraSPARC sfmmu - 生き残るSpitfireの精神 UltraSPARCとともに
UltraSPARCのMMU その名は、「ガミガミ女」のMMU...
前回NiagaraではMMU用のTrapが違うようだっていうお話をしましたので、その"流れ"で今回はUltraSPARCのMMUについて、ちょっとお話しましょう。前回は、確か、次の2つのトラップ(TL = 0の時)が従来のUltraSPARC用のMMUトラップ…っていうお話をしたんでしたよね? 今回はその続き…。
さぁ、始まり始まり…。^_^
ITLB_MISS(tt0); /* 064 instruction access MMU miss */ DTLB_MISS(tt0); /* 068 data access MMU miss */
と言いつつ、さて皆さん、出だしからいきなり脱線です ^-^ UltraSPARC用のMMU処理はsfmmuと呼ばれているんですけど、このsf...というのは、初代のUltraSPARC CPU, UltraSPARC-IのコードネームがSpitfireであったことからきたもの - すなわちSpitfire MMUの略記なんですねぇ。
スピットファイアというと、あの第2次世界大戦中のイギリスの名機を思い出す方もいるのでは? バトル・オブ・ブリテンで、ドイツのメッサーシュミットと互角の戦いを繰り広げた戦闘機ですねぇ。子供の頃、スピットファイアやメッサーシュミット、零式戦闘機なんて聞くと、何か凄いもののように思えて、胸がドキドキしたものです。^O^
もちろん、今回はその戦闘機うんぬんということでは全然ないんですが、…全然今まで知らなかったのですが…、このスピットファイア、「ガミガミ女」っていう意味(注1)があるらしいんですよねぇ。('-')
つまり、UltraSPARCのsfmmuって、ガミガミ女のMMUっていうことですよね。この新鮮な驚きっ! (← これが言いたかった (^-^;)>)
それでは、しょっぱな早速脱線したところで、このTLBミスって何?っていうところをまず説明しましょう!^.^
MMU(= Memory Management Unit)をサポートしているCPUは、ご存知のようにメモリにアクセスする時にVA(= Virtual Address:仮想アドレス)でアクセスすることができます。でも、メモリの実体は、PA(= Physical Address:物理アドレス)で番地付けられていますよね。プログラムがメモリの番地をVAで指定してきた時に、「誰か」がVAからPAに変換してCPUに本当の番地を教えてあげないといけないわけです。そしてそれをMMUがやっている…というわけですね。それで、プログラムは仮想アドレスを番地として使っても問題ないわけですね。
それでは、MMUはどのようにして仮想アドレスを物理アドレスに変換しているかというと、TLB(= Translation Lookaside Buffer)という変換テーブルを用意していて、そのテーブルに該当のアドレスがないかを検索します。見つかったら、その情報を元に変換してやるわけです。X86アーキテクチャでは、Page Translation Cacheと呼ばれてたりしますから、こちらの方が馴染みがある方もいるでしょうね。
まぁ、運良くTLBに探しているアドレスがあればいいんですけれど、世の中そう甘くな〜いっ。いつもあるとは限らないお金とアドレス、必要な時に見つからない。じゃぁ、どうすればいいのぉ? そう、そう、そういう時は、嘆きたくなりますよねぇ、なんとかしてぇぇって。 MMUが嘆きたくなるその時、CPUがtrapしてくる - そう、それがこのTLBミス・トラップなんです。(^-^;) .oO (ちょっと苦しかったかしら…)
実際には、TLBミスが発生した時にどうするのかっていうのは、CPU次第。X86アーキのように、カーネルがPage Directory Table等々のTableを用意してあげて、CPUがTable Walkingを行って捜し出すものもあれば、CPUがtrapをあげてカーネルに教えて頂戴って言ってくるのもあります。
UltraSPARCは後者を採用しているわけなんですね。X86アーキに慣れている人は、なんか面倒だなって感じるかも知れないけど、この手法は結構ポピュラーな方法です。Andrew S. Tanenbaum教授の(有名な)著書"Operationg Systems Design and Implementation"にもSoftware TLB Managementとして説明(注2)されていて、数々のRISC CPUに採用されている方法でもあるんですね。(*_*)
さぁ、相変わらず前置きが長い私です…さっさと本題に入りますと(^.^;)(汗汗)、このITLB_MISSとDTLB_MISSは、今まで説明したように、基本的にはMMUがTLBを使って検索したが見つからなかったアドレス変換を、Nucleusが肩代りしてあげるためのものです。
ほぼ双子の二人なITLB_MISS()とDTLB_MISSですから、どちらか一方、ではDTLB_MISS()の方をみてみましょうね?(インストラクションには実行権が必要なので、その辺りが違ったりしますが、まぁ気にしないことにしませう)
NucleusにはTSB(= Translation Storage Buffer)と呼ばれる領域があって、これでTLBエントリ(TTE)を管理しています。いわば、Nucleusが管理しているDirect Map Cacheのような"感じ"なんですが、MMU miss trapが発生しますと、CPUはこれかなって思う8Kページの情報(TSBのポインタやTag情報)を持ってシステムにtrapをかけてきますので、この情報を元に、TSBからアドレス変換情報を引っ張ってきて、TLBを更新してあげる…というのが、このUltraSPARCのMMU miss trapの基本処理です。(^-^)
それでは、説明はそれぐらいにして、実際のコードをみてみましょうね。
/* * Needs to be exactly 32 instructions * * UTLB NOTE: If we don't hit on the 8k pointer then we branch * to a special 4M tsb handler. It would be nice if that handler * could live in this file but currently it seems better to allow * it to fall thru to sfmmu_tsb_miss. */ #define DTLB_MISS(table_name) ;\ .global table_name/**/_dtlbmiss ;\ table_name/**/_dtlbmiss: ;\ HAT_PERCPU_DBSTAT(TSBMISS_DTLBMISS) /* 3 instr ifdef DEBUG */ ;\ mov MMU_TAG_ACCESS, %g6 /* select tag acc */ ;\ ldxa [%g0]ASI_DMMU_TSB_8K, %g1 /* g1 = tsbe ptr */ ;\ ldxa [%g6]ASI_DMMU, %g2 /* g2 = tag access */ ;\ sllx %g2, TAGACC_CTX_LSHIFT, %g3 ;\ srlx %g3, TAGACC_CTX_LSHIFT, %g3 /* g3 = ctx */ ;\ cmp %g3, INVALID_CONTEXT ;\ ble,pn %xcc, sfmmu_kdtlb_miss ;\ srlx %g2, TAG_VALO_SHIFT, %g7 /* g7 = tsb tag */ ;\ brlz,pn %g1, sfmmu_udtlb_slowpath ;\ nop ;\ ldda [%g1]ASI_NQUAD_LD, %g4 /* g4 = tag, %g5 data */ ;\ cmp %g4, %g7 ;\ bne,pn %xcc, sfmmu_tsb_miss_tt /* no 4M TSB, miss */ ;\ mov %g0, %g3 /* clear 4M tsbe ptr */ ;\ TT_TRACE(trace_tsbhit) /* 2 instr ifdef TRAPTRACE */ ;\ stxa %g5, [%g0]ASI_DTLB_IN /* trapstat expects TTE */ ;\ retry /* in %g5 */ ;\ unimp 0 ;\ unimp 0 ;\ unimp 0 ;\ unimp 0 ;\ unimp 0 ;\ unimp 0 ;\ unimp 0 ;\ unimp 0 ;\ unimp 0 ;\ unimp 0 ;\ unimp 0 ;\ .align 128
まず、上の青色で表示されたところをみてください。
これがTLB miss Trapの処理の中核部分です。どうですか? 凄くシンプルですよね? Tanenbaum教授が「過去はHWでMMUを処理していたが、現代のRISC CPUはそれをソフトで行う…。驚くべきことに、…ソフトで行う処理は非常に効率的…」と記述したところ(正確には注2を見てね)が、正にここ。これをみると、なるほどと頷けるものがありますよね。^-^
赤色で示されたところは、TSBにもない場合や、8Kページではなかった場合、コンテキストが不正である場合などの時に、飛んでいくルーチンです。実際には、色々なケースがあるので一筋縄ではいかないんですよね…^.^;。でも、基本は凄くシンプルってことが見て頂けたでしょうか? RISCらしさ- シンプルであること - が出ているところ…かも知れませんね。^-^ (8kページ以外や異常系はそれなりに大変だけど…)
そうそう、コメントなんですけれど…
/* * Needs to be exactly 32 instructions * * UTLB NOTE: If we don't hit on the 8k pointer then we branch * to a special 4M tsb handler. It would be nice if that handler * could live in this file but currently it seems better to allow * it to fall thru to sfmmu_tsb_miss. */この、「exactly 32 instructions」 ← ここに前回の疑問の回答がありました!のでご報告。(^-^)v
CPU実装依存のTrap 0x64 (instruction access MMU miss)とTrap 0x68 (data access MMU miss)は、UltraSPARCでは32命令まで使用できることになっているんですね。それに対し、SPARCv9で決められているMMU用のTrap 0x9と0x31は8命令。この違いから来ているみたい。
普通、Trapハンドラのエントリは8命令で記述するんですけれど、例外はWindow Registerを扱うspill/fill/clean_windowトラップ。こちらは、性能に関係することもあって、32命令まで使用できるようになっています。でも、このinstruction access MMU missとdata access MMU missもspill/fill君たちと同じく、32命令なんですね。これで前回の疑問がとけました。(嬉しいっ)
なぜUltraSPARCはMMU用のTrap 0x9と0x31を使用せず、代わりに0x64と0x68を使用することにしたか - それは、TLB miss trapの処理にはスピードが必要なのにもかかわらず、8命令では命令数が足りなくて処理が遅くなってしまうから - ということだったんですね。(うんうん。納得しましたっ)
そういえば、このトラップ番号0x64, 0x68は、それぞれfast_instruction_access_MMU_miss trap, fast_data_access_MMU_miss trapと、やたら長い(^.^)名前で呼ばれていました。(ってすぐ気づけって…。ははは…。(^-^;)> でも分かって良かった)
......
今日は「ガミガミ女」のMMU(← こだわりです'-') - sfmmuの基本的な部分をさっくり覗いてみました。如何だったですか? HWではなくSWで処理するMMUの、シンプルな基本デザインとそこからくる処理速度。Tanenbaum教授を驚かせた意外性がここにありました。UltraSPARC-I以降のUltraSPARC達ががそれぞれMMUの実装を変更しつつ(注3)も、この「ガミガミ女」のデザインはしっかり踏襲しています(^.^;)。
......
さて、お知らせです。
実は7/4の独立記念日の休暇を利用して、日本でビザを更新することにしました。生憎ビザ更新には時間がかかることもあり、しばらく日本に滞在することになりそうです(幸か不幸か…ですね(^-^).oO(お刺身食べたい…))。そんなこともあって、僕のBlogも不定期になりそうなんですけれど、どうかお許し下さいね! ちょっと予定が分からないので、予めご連絡しておきたいと思いました。それでは。(^v^)
◎ (注1) スピットファイアって「ガミガミ女」以外にも色々意味はありそうなのですが…(+_+)
◎ (注2) Tanenbaum教授は、「... In this design, TLB management and handling TLB faults are done entirely by the MMU hardware. Traps to the operating system occur only when a page is not in memory. In the past, this assumption was true. However, some modern RISC machines, including the MIPS, Alpha, and HP PA, do nearly all of this page management in software. .... Suprisingly enough, if the TLB is reasonably large (say, 64 entries) to reduce the miss rate, software management of the TLB turns out to be quite efficient. ...」と説明されています。
引用は、"Operationg Systems Desing and Implementation Second Edition" (ISBN 0-13-630129-9) P.329 から。
◎ (注3) 実はSPARC architectureはMMUの仕様については明確に定めていません。v7では存在せず、v8ではReference MMUがあり、v9ではMMU Requirementが定義されているだけです。V9のMMU Requirementについては、"The SPARC architecture Manual Version 9"のF SPARC-V9 MMU Requirement (P275)を見て貰うとよいと思いますが、他との整合性を満たすための必要な事項等が定められているだけで、そのRequirementを満たすのであれば、実際にはどのようなMMUの実装であってもかまいません。
MMUは性能に大きく関係するところであり、SPARC CPUの設計の見せどころになっています。また近い将来、技術の進歩により実装が変更される場合でも、システムの互換性を損なわず新しい技術を導入できるようになっています。極端な話として、MMUを実装しなくてもArchitecture上は全く問題ありません。ですが、MMUの違いはカーネルの実装で吸収しなくてはなりません。(T-T)
Technorati Tag:
SPARC
Technorati Tag:
OpenSolaris
Technorati Tag:
Solaris
Technorati Tag:
trap
Technorati Tag:
UltraSPARC
Technorati Tag:
sfmmu
Technorati Tag:
MMU
Posted at 04:38午前 7 02, 2005 by eota in kernel | Comments[0]
Niagara Nucleus - まだHWがナイあガラ ← 苦しい^.^;
Niagara Nucleus処理を覗きみる (滝に打たれに脇道へ)
いつもは激暑になることが多かった初夏ですけれど、今年はなぜか過ごし易い日々が続きます。朝晩は冷え込んで肌寒い事も…。いつもですと、昼間は滝のように水を浴びたくなるのに、今年はそうでもないんですねぇ。嬉しいのか、悲しいのか、ちょっと微妙。(*.*)さて、前回お話したSolarisのNucleus処理ですけれど、Trap Tableのお話をちょっとしました。先日リリースされたOpenSolarisのソースですが、SPARCv9用のtrap_table.sが2つありますよね? 気づきました?
先日のOpenSolarisのソースは、OpenSolarisと銘打っているものの、Sunで開発しているSolarisのソースとほとんど変わりません。6月上旬のソースをそのまま公開しましたので、かなり出来たてほやほやのやつを、生きのいいまま(?)出したって感じになってます。(^-^;)
2つのtrap_table.s - sun4u/ml/trap_table.sとsun4v/ml/trap_table.sなんですが - sun4uのものは従来のUltraSPARCのものなんですけれど、sun4vの方はNiagara用なんですねぇ。(おいおい…^-^;)
実機もまだ巷に出回っていないのに、どうするんじゃぁ?って思いますが、まぁ、そのまんまですから…。ははは。^^; 当然sun4vの方はバグがまだまだあるってわけですね(でも、ハードが未だ出ていないから関係ないですねっ。うんうん!)
では、Niagara向けはどの辺が違うのか、さっそく覗き見してしまいませう。こういうことが、どうどうとできてしまうのが、ある意味オープンソフトのいいところっ!
では、さっそくNiagaraの滝を浴びに…ではなく、Niagaraのtrap table(^.^)をさっくりみてみましょうね。
まず目につくところは、trap_table0のところの、次の2つのMMU missトラップ(0x009と0x031)でしょうか?
ITSB_MISS; /* 009 instruction access MMU miss */ DTSB_MISS; /* 031 data access MMU miss */
これらのトラップはSPARCV9ではMMU用のトラップとして定義(注)されていますが、従来のUltraSPARCアーキでは(なぜか)使用せず、代わりに次の実装依存のトラップをMMU用に使用していました。
ITLB_MISS(tt0); /* 064 instruction access MMU miss */ DTLB_MISS(tt0); /* 068 data access MMU miss */
今回、このトラップ番号0x009と0x031はSunのSPARC機(というか、SPARCv9 CPUかな?)として、初めて使用されることになりますね。
このトラップをもう少し調べると、発生時に次のマクロによりsfmmu_slow_{d,i}mmu_missにジャンプすることが分かります。
#define DTSB_MISS \ GOTO_TT(sfmmu_slow_dmmu_miss,trace_dmmu) #define ITSB_MISS \ GOTO_TT(sfmmu_slow_immu_miss,trace_immu)
これらの関数は、sfmmu/ml/sfmmu_asm.sで定義されており、コメントには次のようにありますから、やはりsun4vで新たにサポートされたトラップであることが分かりますよね。どうやら、{instruction, data} miss with multiple TSBsの時に発生するもののようですねっ。
#ifdef sun4v /* * User/kernel data miss w// multiple TSBs * The first probe covers 8K, 64K, and 512K page sizes, * because 64K and 512K mappings are replicated off 8K * pointer. Second probe covers 4M page size only. * * MMU fault area contains miss address and context. */
もう1つ目につくのは、次のトラップ番号0x60がサポートされていないことです。
NOT; /* 060 interrupt vector */
従来であれば、割り込みはこのトラップ番号0x60でシステムに通知されるのですが、その代わりに次の2つのmondoトラップが用意されています。
CPU_MONDO; /* 07C cpu_mondo */ DEV_MONDO; /* 07D dev_mondo */
これらの関数はsun4v/ml/mach_interrupt.sで定義されており、従来interrupt vectorとして通知されていた割り込みが、sun4v上ではcpu割り込み(0x07c)とdevice割り込み(0x07d)に別れたことが分かります。恐らく、これは従来cpu間の通信手段として使われていたx-call(クロスコール)と通常のデバイス割り込みが同じトラップ番号0x60を共有していたため、それを改めて明確に分離したものと思われます。
そもそもクロスコールとデバイス割り込みとは性格が違うものなので、分離するのはある意味では"筋"であったかも知れませんね。
また、面白いのは、エラー割り込み(CE=ECCを使ったコレクタブルエラーなど)として使われていた次のトラップが無くなり、
GOTO_TT(ce_err, trace_gen); /* 063 corrected ECC error */ LABELED_BAD(tt0_fecc); /* 070 fast ecache ECC error */ LABELED_BAD(tt0_dperr); /* 071 Cheetah+ dcache parity error */ LABELED_BAD(tt0_iperr); /* 072 Cheetah+ icache parity error */
代わりに、resumable, non-resumableという形でまとめられています。
GOTO_TT(resumable_error, trace_gen); /* 07E resumable error */ GOTO_TT(nonresumable_error, trace_gen); /* 07F non-reasumable error */
これらのトラップは実際にはsun4v/os/error.cで処理されていますが、ざっと見では、今のところは、あまり従来の枠を越えたものには見えませんね。残念っ(今後変わるかな?)。
ところで皆さん、sun4v用のtrap_table.sを読むと、hypervisorという言葉が使われている事に気づきました? ^-^
/* * Data miss handler (must be exactly 32 instructions) * * This handler is invoked only if the hypervisor has been instructed * not to do any TSB walk. * * Kernel and invalid context cases are handled by the sfmmu_kdtlb_miss * handler. * * User TLB miss handling depends upon whether a user process has one or * two TSBs. User TSB information (physical base and size code) is kept * in two dedicated scratchpad registers. Absence of a user TSB (primarily * second TSB) is indicated by a negative value (-1) in that register. */
hypervisorというのは、いわゆる仮想化技術のことで、有名なものにはVMwareがありますが、hypervisorはどちらかというとXenのようなIBMのVM(Virtual Machine)技術に似た仮想化技術です。
sun4vにはhypervisorが使われると予想できますけれど、実際sun4v/ml/hcall.sを覗いてみると、hypervisorの処理が幾つか実装されていますねぇ。(あるある!)
これらのhypervisorの関数をみていますと、次のように%o5レジスタにFunction Numberを入れて、ソフト・トラップをかけることが分かります。
mov SVC_RECV, %o5 ta FAST_TRAP
これらの値は、sun4v/sys/hypervisor_api.hに定義されていますので、興味がある方はそちらを見てみて下さいね。
FAST_TRAPは、0x80で定義されていますので、実際にはトラップ番号0x180でシステムに通知されるはずですが、sun4v用のtrap_table.sを見ますと、
/* * Code running at TL>0 does not use soft traps, so * we can truncate the table here. * However: * sun4v uses (hypervisor) ta instructions at TL > 0, so * provide a safety net for now. */ /* soft traps */ BAD4; BAD4; BAD4; BAD4; /* 100 - 10F unused */ BAD4; BAD4; BAD4; BAD4; /* 110 - 11F unused */ BAD4; BAD4; BAD4; BAD4; /* 120 - 12F unused */ BAD4; BAD4; BAD4; BAD4; /* 130 - 13F unused */ BAD4; BAD4; BAD4; BAD4; /* 140 - 14F unused */ BAD4; BAD4; BAD4; BAD4; /* 150 - 15F unused */ BAD4; BAD4; BAD4; BAD4; /* 160 - 16F unused */ BAD4; BAD4; BAD4; BAD4; /* 170 - 17F unused */ /* reserved */ NOT4; NOT4; NOT4; NOT4; /* 180 - 18F reserved */
とあり、残念なことにまだ実装されていない様子です。(^_^;)
今後、sun4v/Niagaraの実装がリリースされることを期待したいですね。
今日は、ちょっと脱線してNiagaraのNucleusの実装を覗きみました。
ソースが公開されていると、HWが出ていなくても、どんな感じのものなのか、 ちょっと想像できてしまいます。なんとなくNiagaraが今から楽しみになってきてしまいますね。
(注) トラップ番号0x09と0x31は、SPARCv9では次のように定義されています。でも、今で使われていなかったんですよ。う〜ん、不思議だ。Niagaraのためにとっておいた…とも思えんし。(?_?)
● instruction_access_MMU_miss [tt=0x09]
A miss in an MMU occurred on an instruction access from memory. For example, a PDC or TLB did not contain a translation for the virtual adddress.
● data_access_MMU_miss [tt=0x31]
A miss in an MMU occurred on a data access from/to memory. For example, a page descriptor cache or translation lookaside buffer did not contain a translation for the virtual address.
引用は、"The SPARC architecture Manual Version 9" P112, P113から。
Technorati Tag:
OpenSolaris
Technorati Tag:
Solaris
Posted at 12:05午前 6 22, 2005 by eota in kernel | Comments[0]
祝OpenSolaris:誕生おめでとう!
Solaris Nucleus処理の解説 (SPARC版)
今日はOpenSolarisの誕生日! v(^-^)/ 待ちに待った(?)日です。生まれたばかりのOpenSolarisは、まだまだヨチヨチの赤ん坊。皆さん、暖かいご支援を期待したいです!
さて、いよいよソースコード[1]もどうどうと皆さんの前にさらけ出しても、なんらお叱りを受けることもなくなりましたね。一つ一つ手かせ足かせが取れていくようで、嬉しい限りです。
今日は、これからOpenSolarisをやろうという人のため、今までとはうって変わって、真面目に実装の解説をすることにしました。(といっても、今までも、それなりに真面目のつもりだったんですが‥ (^^;)
そして今日の解説は、迷いもなく(^-^;)、Solarisの心臓部Nucleus[2]。(じゃじゃーーん)
どのOSもException/Interruptを処理して初めてOSと呼べると思うのですが、(←単に持論です、無視して頂いてかまいませんです。はい^-^) Solaris Operating Systemでは、Exception/Interruptを処理するtrapレベルのコードをnucleusと呼んだりします。このnucleusというのは、SPARC v9アーキテクチャ[3]で定義されている言葉なんですが、ちょっと説明しますね。
SPARC V9では、MMUのコンテキストとして、次の3つをサポートできることになっています。
- Primaryコンテキスト
- Secondaryコンテキスト
- Nucleusコンテキスト
このうち、最初のPrimaryコンテキストとSecondaryコンテキストは必須です。幸い(?)なことに、SunのUltraSPARCシリーズと富士通のSPARC64[4]では、Nucleusコンテキストをサポートしていますので、ここで解説するお話は、双方のSPARC V9アーキのCPUに当てはまりますね!。
Solaris Operating System(面倒なので、以下は、Solaris OSと略記しちゃいます)では、trapを処理する部分[5]はusr/src/uts/sun4u/ml/trap_table.sです。
でもちょっと待って下さいね。SPARC用のtrap_table.sを読む前に、SPARC V9特有のNested Trapについて知っておかないといけませんよぉ。
Nested Trapというのは、Exception/Interruptを処理している最中でも、trap、Exceptionなどの処理をできる仕組みをいいます。
つまり、trap処理であっても、trapがdisableされていない - enable状態になっているということなんですね。これを処理するために、CPUでは、trap stackを実装する必要がありますが、何段まで実装するかというのは、CPUの実装依存になっています。
では、UltraSPARCではどうなっているんでしょう?
1222 /* 1223 * ======================================================================= 1224 * SPARC V9 TRAP TABLE 1225 * 1226 * The trap table is divided into two halves: the first half is used when 1227 * taking traps when TL=0; the second half is used when taking traps from 1228 * TL>0. Note that handlers in the second half of the table might not be able 1229 * to make the same assumptions as handlers in the first half of the table. 1230 * 1231 * Worst case trap nesting so far: 1232 * 1233 * at TL=0 client issues software trap requesting service 1234 * at TL=1 nucleus wants a register window 1235 * at TL=2 register window clean/spill/fill takes a TLB miss 1236 * at TL=3 processing TLB miss 1237 * at TL=4 handle asynchronous error 1238 * 1239 * Note that a trap from TL=4 to TL=5 places Spitfire in "RED mode". 1240 * 1241 * ======================================================================= 1242 */上のコメントを読みますと、UltraSPARCは5段の深さを持っていることが分かりますね。何段であるかはCPUの実装依存なんですけれど、カーネルを移植するためには重要な意味を持っています。
ここで、TL=0というのが通常のカーネル/ユーザモードです。そして、例外や割り込みが発生すると、TLレベルが1つあがって、TL=1となります。そして、この時にコンテキストがNucleusコンテキストに切り替わります。Nested Trapの場合は、この状態でも更に例外などを受け付けることができ、上のコメントにありますように、window fill/spillトラップ、MMUトラップなどを受け付けることができます。
ですけれど、stackには限りがありますよね。永遠にtrapを受け付けることはできませんので、上限にぶつかってしまった場合は、REDモード[6]と呼ばれる特別なモードに入ります。REDモードは、異常処理を扱うモードで、通常はここに入ることはありませんけれど。(でも、バグっていると、すんなり(^-^;)入ります)
さて、SPARC V9用のtrap tableですが、実装では、TL = 0の時と、TL > 0の時の2つの表に分割されます。TL = 0用(通常のtrap処理用)は、trap_table0でスタートする表で、TL > 0用 (nested trap用)は、trap_table1でスタートする表です。ソースでは、次のようになっています。
TL = 0用(通常のtrap処理用)
1250 trap_table0: 1251 /* hardware traps */ 1252 NOT; /* 000 reserved */ 1253 RED; /* 001 power on reset */ 1254 RED; /* 002 watchdog reset */ 1255 RED; /* 003 externally initiated reset */ 1256 RED; /* 004 software initiated reset */ 1257 RED; /* 005 red mode exception */ 1258 NOT; NOT; /* 006 - 007 reserved */ 1259 IMMU_EXCEPTION; /* 008 instruction access exception */ 1260 NOT; /* 009 instruction access MMU miss */ 1261 ASYNC_TRAP(T_INSTR_ERROR, trace_gen); 1262 /* 00A instruction access error */ 1263 NOT; NOT4; /* 00B - 00F reserved */ 1264 ILLTRAP_INSTR; /* 010 illegal instruction */ 1265 TRAP(T_PRIV_INSTR); /* 011 privileged opcode */ 1266 NOT; /* 012 unimplemented LDD */
TL > 0用 (nested trap用)
1412 trap_table1: 1413 NOT4; NOT4; NOT; NOT; /* 000 - 009 unused */ 1414 ASYNC_TRAP(T_INSTR_ERROR + T_TL1, trace_gen); 1415 /* 00A instruction access error */ 1416 NOT; NOT4; /* 00B - 00F unused */ 1417 NOT4; NOT4; NOT4; NOT4; /* 010 - 01F unused */ 1418 NOT4; /* 020 - 023 unused */ 1419 CLEAN_WINDOW; /* 024 - 027 clean window */ 1420 NOT4; NOT4; /* 028 - 02F unused */ 1421 DMMU_EXCEPTION_TL1; /* 030 data access exception */ 1422 NOT; /* 031 unused */ 1423 ASYNC_TRAP(T_DATA_ERROR + T_TL1, trace_gen); 1424 /* 032 data access error */ 1425 NOT; /* 033 unused */ 1426 MISALIGN_ADDR_TL1; /* 034 mem address not aligned */ 1427 NOT; NOT; NOT; NOT4; NOT4 /* 035 - 03F unused */ 1428 NOT4; NOT4; NOT4; NOT4; /* 040 - 04F unused */ 1429 NOT4; NOT4; NOT4; NOT4; /* 050 - 05F unused */ 1430 NOT; /* 060 unused */
同じような表が2つあることが見て取れますね。右に書かれている数字がトラップ番号で、該当するトラップが発生した時に処理するルーチンがこの表で定義されているわけですね。例えば、TL = 0の表でトラップ番号008を見ますとinstruction access exceptionが発生した時に、IMMU_EXCEPTIONが実行される、というように分かりますよね。
Solaris OSのTrap処理を理解するには、まずこのtrap_table.sを理解するのが第一歩ですけれど、これからポツリポツリとSolarisの実装について(つまみ食い)解説していくつもり(今のところ‥^.^;)ですのでお楽しみに!
1. 一応、ライセンスがついているようです。(^-^;)
2. サンに来る前に働いていた富士通のプライムパワー用のSolarisで最初にいじったところです。その時はプライムパワー用のSolaris RAS処理を実装しました。
3. The SPARC Architecture manual (SPARC International, Inc) ISBN: 0-13-099227-5を参照して下さいね。
4. SPARC64-III, -IV, -V(Vはちょっとだけ)知っています。しっかりした、いいCPUだと個人的には思っています。サンも富士通も好きなので、サンと富士通が仲良くできてとても嬉しいです。APL、可愛がってね。
5. 個人的な好みでは、trap処理のようなlowレベルのコードが大好きです。
6. REDモードは、SPARCv9のスペック策定の時、富士通側の要望により入ったものだと、当時一緒に働いていた富士通のHWのエンジニアの方から教わりました。
Technorati Tag: OpenSolaris
Technorati Tag: Solaris
Posted at 01:23午後 6 14, 2005 by eota in kernel | Comments[0]
leakにも強いSol10のmdb:あぁぁなんて便利なのぉの巻
皆さん、こんにちは。 週末は如何でしたか? すっかり間が空いてしまい、ちょっとご無沙汰って感じです。(^-^).oO (Gobusata Desu!) 障害調査やらI/Oメモリやらで、カラカラと走り回っていたんですけれど、それに加え、 娘の通う日本語補習校のボランティアにはまってしまいました。はは。。。^.^; ボランティアで、 Webアプリを作っているんですけど、普段の開発とは畑違いの世界。でも、Webアプリに 興味があったので(流行ものに弱い)、ちょっと深入りしてしまったらはまってしまいました。^0^ MySQL + PHP + Javascriptなんで、完全にインタープリターの世界。 すぐ結果がみれる‥デバックもサクサク快感シリーズ。 あぁぁこのサクサク感がいいぃって浸ってしまい、ついつい夜更かしを繰り返し、ついには 家でも会社でも、昼でも夜でもプログラムを作っている有様。。。妙に若い頃を思い出してしまいました。 たまには違うプログラミングも楽しいって分かったこの頃です。(^_^) ちょっと脱線してしまいましたけど、今日も助っ人mdb君のお話です。 やっぱりプログラミングではデバックもサクサク快感を味わいたいモノ。 Solaris君にはmdbがついているさっていうことで、普通なら頭を抱えてしまいがちな、 メモリリークをmdbでささぁ、さくさくっていうところをご紹介です。 メモリリークというトラブルは、何にもないと**とてつもなく、そして限りなく**大変。 (はぁぁ‥考えただけでも、重くなってしまうぐらい‥) トラブルの状況も、マシンがだんだんと重くなって‥まるで足に1つ1つ重石を繋がれている ように、徐々に先に進まなくなってきて‥そして、もうズリズリと最後は這っているかのような‥ こういうトラブルに見舞われたら、あぁあなたのマシンにもメモリリーク居るかも? メモリリークは原因を探るのは難しいくせに、ワークアランドは簡単明快。そうです、マシンを リブートしちゃうんです。そうすると、それまで溜めに溜めた重石を全部捨て去るように、 いきなりサクサクに戻りますよね。でも、また数日して徐々に重くなっていく感じが復活。(--;) 仕方無しに、定期的にリブートするようにするかぁぁっていう体験はありませんか? そうです、それがメモリリークくんなんです。困ったものですねぇ。 さて、この困り者のメモリリークくん、調査しようとなると、まずはパニックではないので、 メッセージも何もありません。遅くなったら強制的にクラッシュダンプを取るのが第一歩です。 でも、そこからが地道な作業の始まり‥カーネル内のメモリの使用状況をみて、どの子が重りを つけられ、アップアップしているか探し、やたら巨大化して肥満状態になった子をみつけたら、 その子がどこでやたらメモリを食べているかを探す‥っていうのが、今まででした‥。^^; でも何も情報がないのでは、砂漠に埋もれた指輪を探すようなもの。壮大な砂漠を目前に途方に くれてしまいますね。(T-T) でもSolarisの場合は、嬉しい機能が幾つかあって、壮大な砂漠を抜ける道を教えてくれるので 本当に助かるなぁ。"-" メモリリークで当たりをつけるには、メモリにロギング機能がないと話になりません - この子が 重そうだって分かっても、何も情報がないのでは、結局は巨大なジャングルのようなソースコードを 走り回るようなもの。たっぷり汗をかくわりには、結局迷子になりかねませんよね。^.^ Solarisは、カーネルメモリのアロケートにslab allocator (object-caching kernel memory allocator) を使用しています。Jeff Bonwickさんが作成したものですが、大変 有名なもので、数々のOSに影響を与えているカーネルメモリ・アロケーターです。最近ではLinux/BSDも 似たような実装を行っています。興味がある人は、次のUsenixの論文に目を通されてもよいし、巷で 売られているカーネル本にも簡単な解説があったりします。(でも、ちょっと難しいので‥心してね^-^) Jeff Bonwickさんの(最初の)論文。 The Slab Allocator: An Object-Caching Kernel Memory Allocator (1994) カーネル本には、例えば、Uresh VahaliaさんのUnix Internals: The New Frontiers. (Prentice Hall, 1996)が有名かも知れません。 まぁ、slab allocatorの実装の詳細はそちらに譲るとして、slab allocatorには、 object-cachingによる性能改善以外に強力なログ機能があって、このログ機能が、メモリ破壊や メモリリークに一役かっているんですよぉ。このログ機構のお陰で、メモリ破壊の調査がやたら楽。 涙ポロポロものなんですけど、mdbのマニュアルとかを見て貰うと、何となく分かって貰えるかも? さて、メモリリークくんには、そのものズバリの"findleaks"というコマンドがmdbには用意されていて、 こいつを使ってやれば、あっという間のリークくん(←なんじゃらほい?)になるんですよ。 では、ちょっと見てやりましょうね。(前置きながかった?かしら‥) さぁ、トラブルが発生、あなたはクラッシュダンプを取りました。トラブッた人の話では、 どうやらメモリリークの様子。 でも大丈夫。findleaksで一発! さぁいきますよぉ。
> ::findleaks
CACHE LEAKED BUFCTL CALLER
000003000006f688 1 00000300088fada8 cpr_isset+0x14
000003000006f688 1 0000030008d72da8 cpr_write_header+0x64
000003000006f688 1 0000030008143570 devi_attach+0xcc
000003000006f688 1 000003013875e280 devi_attach+0xcc
000003000006f688 1 000003004461d938 devi_attach+0xcc
000003000006f688 1 000003000a819738 devi_attach+0xcc
000003000006f688 1 00000300210add68 devi_attach+0xcc
000003000006f688 1 00000300022b67c8 devi_attach+0xcc
000003000006f688 1 00000300095cbdf0 devi_attach+0xcc
000003000006f688 1 000003000994c038 devi_attach+0xcc
0000030001453688 1 0000030008f0d4f8 dv_mknod+0x28
000003000006f688 1 000003000acd6e88 dv_mknod+0x44
0000030000073408 1 0000030009217c08 dv_shadow_node+0x4c8
000003000006f688 1 0000030050a3b4d0 frrequest+0xafc
000003000006f688 1 000003000964a268 ibt_query_qp+0x108
0000030000077188 1 00000300009fb6b0 kobj_export_ksyms+0x11c
000003000007a008 1 00000300009f6868 kobj_export_ksyms+0x11c
0000030000077b88 1 00000300009eb5a8 kobj_export_ksyms+0x11c
000003000006f688 1 0000030000cc5b88 kobj_zalloc+8
000003000006f688 3 00000300087d6938 kobj_zalloc+8
000003000006f908 1 00000300091f1180 nv_mem_zalloc+0x10
000003000006f908 1 0000030008084338 nv_mem_zalloc+0x10
0000030000072008 1 0000030001f61cc0 nv_priv_alloc+0x10
0000030000072008 1 0000030001f5f630 nv_priv_alloc+0x10
000003000006f688 1 00000300087d4d58 pcmcia_get_io_regs+0x26c
000003000006f688 1 0000030008d73a20 rmc_comm_send_pend_req+0xa4
000003000006f688 3 0000030003176208 sppptun_open+0x2e0
000003000108cc88 1 000003000920c9d0 vn_alloc+0xc
=-----------------------------------------------------------------=
Total 32 buffers, 8936 bytes
どうですか? 誰が何回リークしたのか、一発でしょう?
じゃ、その関数のどこでメモリがアロケートされたものか、見るにはどうすればいいでしょう?
それには、BUFCTLで表示されているアドレスを、これまたズバリ"bufctl"コマンドに指定して
やります。例えば‥、
> 00000300088fada8::bufctl -v
ADDR BUFADDR TIMESTAMP THREAD
CACHE LASTLOG CONTENTS
300088fada8 300093c2d68 4f6d0aee3671 2a100cf1cc0
3000006f688 3000049b980 300007ae9a0
kmem_cache_alloc+0x90
kmem_zalloc+0x28
cpr_isset+0x14
i_cpr_save_cpu_info+4
i_cpr_mp_setup+0x2c
devi_attach+0xcc
attach_node+0xdc
i_ndi_config_node+0x148
i_ddi_attachchild+0x64
devi_attach_node+0x5c
devi_attach_children+0x90
config_immediate_children+0xa8
devi_config_common+0xa4
mt_config_thread+0x68
これで、メモリが解放されなかったのは、cpr_isset+0x14番地で獲得されたものだって分かりますね。
やたら簡単過ぎますっ。(-vを忘れないでね!)
findleaksはSol10で強化されているのですが、例えば-dフラグをつけてやると、bufctlを使用する
必要がありませんよ。手が抜けて、嬉しい限り!
> ::findleaks -d
findleaks: using cached results (-f will force a full run)
CACHE LEAKED BUFCTL CALLER
000003000006f688 1 00000300088fada8 cpr_isset+0x14
000003000006f688 1 0000030008d72da8 cpr_write_header+0x64
000003000006f688 1 0000030008143570 devi_attach+0xcc
000003000006f688 1 000003013875e280 devi_attach+0xcc
000003000006f688 1 000003004461d938 devi_attach+0xcc
000003000006f688 1 000003000a819738 devi_attach+0xcc
000003000006f688 1 00000300210add68 devi_attach+0xcc
000003000006f688 1 00000300022b67c8 devi_attach+0xcc
000003000006f688 1 00000300095cbdf0 devi_attach+0xcc
000003000006f688 1 000003000994c038 devi_attach+0xcc
0000030001453688 1 0000030008f0d4f8 dv_mknod+0x28
000003000006f688 1 000003000acd6e88 dv_mknod+0x44
0000030000073408 1 0000030009217c08 dv_shadow_node+0x4c8
000003000006f688 1 0000030050a3b4d0 frrequest+0xafc
000003000006f688 1 000003000964a268 ibt_query_qp+0x108
0000030000077188 1 00000300009fb6b0 kobj_export_ksyms+0x11c
000003000007a008 1 00000300009f6868 kobj_export_ksyms+0x11c
0000030000077b88 1 00000300009eb5a8 kobj_export_ksyms+0x11c
000003000006f688 1 0000030000cc5b88 kobj_zalloc+8
000003000006f688 3 00000300087d6938 kobj_zalloc+8
000003000006f908 1 00000300091f1180 nv_mem_zalloc+0x10
000003000006f908 1 0000030008084338 nv_mem_zalloc+0x10
0000030000072008 1 0000030001f61cc0 nv_priv_alloc+0x10
0000030000072008 1 0000030001f5f630 nv_priv_alloc+0x10
000003000006f688 1 00000300087d4d58 pcmcia_get_io_regs+0x26c
000003000006f688 1 0000030008d73a20 rmc_comm_send_pend_req+0xa4
000003000006f688 3 0000030003176208 sppptun_open+0x2e0
000003000108cc88 1 000003000920c9d0 vn_alloc+0xc
=-----------------------------------------------------------------=
Total 32 buffers, 8936 bytes
kmem_alloc_16 leak: 1 buffer, 16 bytes
ADDR BUFADDR TIMESTAMP THREAD
CACHE LASTLOG CONTENTS
300088fada8 300093c2d68 4f6d0aee3671 2a100cf1cc0
3000006f688 3000049b980 300007ae9a0
kmem_cache_alloc+0x90
kmem_zalloc+0x28
cpr_isset+0x14
i_cpr_save_cpu_info+4
i_cpr_mp_setup+0x2c
devi_attach+0xcc
attach_node+0xdc
i_ndi_config_node+0x148
i_ddi_attachchild+0x64
devi_attach_node+0x5c
devi_attach_children+0x90
config_immediate_children+0xa8
devi_config_common+0xa4
mt_config_thread+0x68
(たくさん表示が続くのでカット...^-^;)
でもちょっと困ることが‥。先もお話しましたけれど、これらの情報はslab allocatorのロギング機能
で取得されたものを表示しているんです。でも、ロギングはデバックカーネルで有効。。。。
それじゃ、困りますねぇー。デバックカーネルじゃないと、findleaksは強化されたものの、
それじゃぁ限定的な情報しか表示してくれませんよねぇ。例えば、
> ::findleaks
CACHE LEAKED BUFFER CALLER
0000030000072308 2 000006000091f0a0 ?
0000030000073508 2 00000300003ac300 ?
0000030000076908 1 00000300000e5b40 ?
0000030000077508 1 00000300000ecac0 ?
0000030000077808 1 00000300000e7000 ?
=-----------------------------------------------------------------=
Total 7 buffers, 8096 bytes
うーん、これじゃ、誰なのか分からない‥。(==;)
ロギングは性能に影響を与えるので、通常はoffなんですよねぇ。それにデバックカーネルは製品には
使われませんし。=.=;
そういう場合には、lightweighなデバックフラグを使うのも手かも知れませんよ。
/etc/systemファイルに、次の行を加えてリブートしてやるんです。
set kmem_flags = 0x00000100
すると、次のようにデバックカーネルでなくても、どの子がリークしてしまったか分かるようになりますよ。
> ::findleaks
CACHE LEAKED BUFFER CALLER
0000030000726c08 1 0000060000a25e40 cpu_init_private+0x54
0000030000073508 2 0000060000874580 init_cpu_info+0xcc
0000030000072308 2 00000600009f5a10 init_cpu_info+0x100
0000030000077508 1 00000300001460c0 kobj_export_ksyms+0x11c
=-----------------------------------------------------------------=
Total 6 buffers, 2888 bytes
このフラグ、富士通がPrimePowerをサポートするのに必要として、導入された軽量なデバックフラグです。
メモリリークだけでなく、メモリ破壊にも大変有効なので、気にならない方は、Onにしておくのを
お奨めしちゃいます。
(富士通のPrimePowerではデフォルトでOnになっている‥はず ^_^)
メモリリークとかメモリ破壊は、本当に大変な障害なんですけど、こういうツールで今は
相当に楽になっていますよね! 嬉しいなって思う機能の一つかな。^-^
それでは。
Posted at 05:53午後 6 13, 2005 by eota in opensolaris | Comments[0]
mdbの強力な助っ人CTF (でも、誰もみてくれない可哀想なやつ)‥の話
Solaris10の新機能ではありませんけれど、今日はSolaris9で導入されたmdbのCTF (Compact ANSI-C Type Format)についてお話したいと思います。 日頃お世話になっているツールですものね。クラフトマンは、ツールを大事にするものですもの。(^-^) 率直に言って、このCTFは、カーネルのデバックには相当な強力な味方になっていて、 一度これにはまると、CTF無しでデバックしたくなくなるほど、便利です。 またデバック時間もかなり節約してくれる、やりくり上手。開発者には必須のアイテムって言っても 良いくらいなんですね。でも、これが黒子、縁の下の力持ちなんですね。 そんな、日頃から陰で支えてくれているのに、陽の当たるところには出ない、CTFについて ちょっと紹介してあげたいって今回は思いました。^_^ oO (Yoroshiku!) CTF以前は、デバック情報を残すためにはコンパイラ・オプションで-gを指定するなどしなくて はなりませんでした。その結果、コンパイルされたカーネルは肥大化してしまい、 32bitの890Kバイト程のunixカーネルが、7Mバイト強まで膨れ上がってしまう有様で、 とても製品版のカーネルに使える状態ではありませんでした。 こんな大きいカーネルではロードにも時間がかかるし、格納媒体も増えてしまいますよね。 (でも、DVDだったら一枚で収まるのかな?) それに、CTF以前のデバックでは、adbのマクロや自分で作成したmdbのコマンドなどを使用して データ構造をクラッシュダンプから出力してやらなくてはならず、ソースが変更されたり、 別のバージョンのカーネルが使用されたりしてデータ構造に変更があったりすると、 表示された結果が妥当なものかすら分からないことがありました。 開発者としては、こっちも相当大きいですよね。adbのマクロやmdbのコマンドを その都度作成していくのは、大変手間暇がかかって、うんざりすることもままありました。 (今だから言える! えへへへへ‥) 結果、各開発者は各々お手製のコマンド、ライブラリを持つことになるんですけれど‥。 (まぁ、自然な流れとして‥) こういう問題を解決するため、C言語のシンボル情報、静的変数、大域変数の情報、リターン値、 引数情報などを効率良くカーネルに残すことを目的に、CTFが作られたわけです。 偉い、さすがっ! って一応エールを送りませう。これを開発したのは、Mike Shaprioさんと Matt Simmonsさんですね。Mikeさんは、元々mdbの開発者です。 そして、MattさんはSolaris10でkmdbを開発しています。(パチパチ "-") CTFの情報に加えて、不要な情報の削除、重複情報の削除、圧縮などを施した結果、 平均して1-2%の増加でデバック情報をカーネルに含ませることができました。 これなら、いつもカーネルに含ませることができますね。(うん、グットだぁ) CTF情報はコンパイル時に作成され、ELFセクションに一度格納された後、 リンカーでロードされる時に、各々のモジュールの持つ情報はマージされて、メモリーにロードされます。 パニックが発生した場合に、自動的にこれらの情報がクラッシュダンプに入るので、 デバック時には動作中のカーネルそのものの情報が得られます。 この仕組みにより、ソース変更や、カーネル・バージョンを気にせずデバックが可能になりました。 そして、Solaris9からmdbはこのCTF情報を使用しており、次のコマンドが導入されています。 ・sizeコマンド ・offsetofコマンド ・printコマンド それでは、ちょっとこれらのコマンドを使うとどういうことになるか、見てみましょうね。(うんうん) まず、psコマンドで、パニック発生時に動いていたプロセスをリストアップさせて、 この中のautomoutdのプロセス構造体を表示させてあげましょう。
> ::ps
S PID PPID PGID SID UID FLAGS ADDR NAME
R 0 0 0 0 0 0x00000001 0000000001842d30 sched
R 3 0 0 0 0 0x00020001 0000030005089b90 fsflush
R 2 0 0 0 0 0x00020001 000003000508a778 pageout
R 1 0 0 0 0 0x42004000 000003000508b360 init
R 101898 1 101898 101898 0 0x42000000 0000030019763398 automountd
R 101881 1 101881 101881 0 0x42000000 000003000f2197f8 nscd
:
automountdのプロセス構造体のアドレスは0x30019763398ですから、
このアドレスをprintコマンドに指定させてあげるだけで、プロセス構造体の中身を見ることができます。
> 0000030019763398::print proc_t
{
p_exec = 0x3001280bd00
p_as = 0x300070617a8
p_lockp = 0x300063f2a00
p_crlock = {
_opaque = [ 0 ]
}
p_cred = 0x300127cf350
p_swapcnt = 0
p_stat = '\002'
p_wcode = '\0'
p_pidflag = 0
p_wdata = 0
p_ppid = 0x1
p_link = 0
p_parent = 0x3000508b360
p_child = 0
p_sibling = 0x3000f2197f8
:
また、プロセス構造体のサイズが知りたい時は、sizeofコマンドを使用して、
> ::sizeof proc_t sizeof (proc_t) = 0xbd0とやってやります。 例えば、プロセス構造体の中にあるas構造体のオフセットが知りたければ、offsetコマンドを使用して、
> ::offsetof proc_t p_as offsetof (proc_t, p_as) = 8とやってやります。 これらのコマンドは、皆クラッシュダンプに格納されているCTF情報を参照してデータ構造(上の例だとプロセス構造体)を把握しているわけですね。実に便利っ! このprintコマンドは、多彩なオプションを実はもっていて、非常に便利。このprintコマンドだけでもかなりデバックがサクサク進む‥。ありがたや、ありがたや。
> ::help print NAME print - print the contents of a data structure SYNOPSIS [ addr ] ::print [-aCdhiLptx] [-c lim] [-l lim] [type] [member|offset ...] DESCRIPTION -a show address of object -c limit limit the length of character arrays -C unlimit the length of character arrays -d output values in decimal -h print holes in structures -l limit limit the length of standard arrays -L unlimit the length of standard arrays -n don't print pointers as symbol offsets -p interpret address as a physical memory address -t show type of object -i interpret address as data of the given type -x output values in hexadecimal type may be omitted if the C type of addr can be inferred. Members may be specified with standard C syntax using the array indexing operator "[index]", structure member operator ".", or structure pointer operator "->". Offsets must use the $[ expression ] syntax ATTRIBUTES Target: kvm Module: mdb Interface Stability: Evolving例えば、-taオプションを指定してやると、先の例は次のようになります。
> 0000030019763398::print -ta proc_t
{
30019763398 struct vnode *p_exec = 0x3001280bd00
300197633a0 struct as *p_as = 0x300070617a8
300197633a8 struct plock *p_lockp = 0x300063f2a00
300197633b0 kmutex_t p_crlock = {
300197633b0 void *[1] _opaque = [ 0 ]
}
300197633b8 struct cred *p_cred = 0x300127cf350
300197633c0 int p_swapcnt = 0
300197633c4 char p_stat = '\002'
300197633c5 char p_wcode = '\0'
300197633c6 ushort_t p_pidflag = 0
300197633c8 int p_wdata = 0
300197633cc pid_t p_ppid = 0x1
300197633d0 struct proc *p_link = 0
300197633d8 struct proc *p_parent = 0x3000508b360
300197633e0 struct proc *p_child = 0
300197633e8 struct proc *p_sibling = 0x3000f2197f8
300197633f0 struct proc *p_psibling = 0
:
このprintコマンドだけを見ても、CTFが如何に便利か分かりますよね。
でも、普段は誰からも意識されない存在。ちょっと可哀想。^^;
皆さん、mdbを使った時に、是非CTF君のことをちょっと思い出してあげて下さいね。(^-^)v
それではよい週末を。
Posted at 01:04午後 6 03, 2005 by eota in opensolaris | Comments[0]
SPARC Memory Model - 涙のハング物語 (またかいっ)
今週悩ませたのは、(またまた?)ハングの問題でした。 でも、今回のハングは、よりシステムプログラムっぽく、いてての問題でした。(++;) そんな僕の涙物語。。。。 眠ったきり、誰にも起こされないThreadがシステムに居たんですけれど、よりによってその子がロックを抱えたまま眠ってしまったんですね。 そのため、そのロックを待っている人たちがずらりと列を作るかのように並んでしまって、システムがにっちもさっちも行かなくなってしまいました。 ちょうと、おトイレの中で眠ってしまって、おトイレを待っている人たちがずらりと並んで、仕事どころではないっという状態でしょうか(って、例えがいいのか微妙ですが。。。。) そもそも、「ロックを抱えたまま眠って」っていうのがあり得ないって感じなんですが、デバイスノードを管理しているルーチンで、こういうことがあり得るものがあるんですね。 今回とは違う例ですけれど、例えば、mutexロックを掴んだthreadがsemaphoreでI/O完了を待っているのに、ちっとも完了しない‥そうこうしているうちに、そのロックを待っている人たちがずらりと並んで‥恐ろしい状況になってしまっている、、というのも似たような(?)感じですね。 # 振り向けばそこに‥たくさんの人たちがぁ‥って怖いですよね、どうでもいいですけれど。(-.-;) 今回のは、どうやらSPARCのメモリモデルと関係しているようでした。 これはちょっと難しい話なので、例をあげましょう。 支払いに小切手を送ったのに、届いていないと電話を貰う。「そんな馬鹿な、今日には届いているはず。ちゃんと郵便ボックスを見てよ?」と言っても、「配達の時間は過ぎている。さっき見たばかり。何も入っていなかった」って言われちゃう。 そんなぁぁぁと思っていると、また電話があって「アパートの人が間違えて入っていたよと君の封筒を持ってきたよ。ごめんね」。 ちっ、人騒がせなやつだ。。 こういうことと似たようなことが起こり得るのが、SPARCのload/store命令です。 上の例を次のように読み替えてみましょう。
小切手を貰う人 = メモリのデータをloadする 小切手を送る人 = メモリにデータをstoreする 郵便を送り、配達時間に届く = プログラムオーダー 隣の人が間違えて持っていった後、実際に郵便が届く = メモリーオーダーそうなると、上の例は次のような現象が起きたことになりますね。(って、ちょっと無理矢理かしら?) メモリにフラグ(データ)をstoreし、その番地からデータをloadしているのに、フラグが見えない。プログラムでは、storeが先でloadが後になっているので、フラグが見えるはず??? ちょっと後で、もう一度念のため見てみると、あれぇフラグが見えている。どうなっているのぉ??? こんな不可思議なことが起きてしまったりするんですねぇ。(*_*;) これは、実はプログラムで書かれているとおりに、loadやstore命令が実行されないことから起きます。えっ? コンパイラやアッセンブラの問題じゃないのぉって? そういう問題もあるかも知れないけど、ここでのプログラムっていうのは、機械語を想定してて、どちらかというとアッセンブラで書かれたプログラムっていう感じに近いですね。 例えば、次のプログラムをCPU1とCPU2で実行し、観測ポイントでそれぞれの結果を確認した時に、CPU1ではレジスタ%r1に1が入っているにもかかわらず、CPU2ではレジスタ%r2には、1が入っていない‥「えぇぇぇぇ、なんだってぇぇぇっ」ということが起きます。(へぇぇぇぇって感じでもあるけど ^-^)
CPU1 CPU2
store #1, [A] store #1, [B]
load [B], %r1 load [A], %r2 {{{{{ 観測ポイントでデータを確認
各CPUではstore→load命令の順に実行するようにプログラムでは指示しているわけなので、それぞれの値は1であるはずですよね?
CPU1ではそのとおりに実行したように見えますが、CPU2はload命令を先に実行してstore命令を実行したみたい。感じ、load命令を先に実行してしまったものですから、CPU1のstore命令がまだ完了していなかったって状況。
ここで、プログラムが指示しているstore→load命令の順をプログラムオーダーといいます。そして、実際にCPUが実行した順番、CPU1ではstore→load命令の順、CPU2では逆のload→store命令の順をメモリオーダーといいます。
つまり、SPARCプロセッサーは、プログラムで指示した順(プログラムオーダー)にメモリにアクセス(メモリーオーダー)するとは限らない‥ということなんですね。(な、なんだとぉぉぉぉ!)
ちょっと頭が痛いです。。。。(かなり頭が痛い???)
実際には、SPARC V9では次の3つのメモリモデルを決めています。
TSO: Total Store Order {{{{{ Load/Store同士の命令の順は守ってくれるモード
PSO: Partial Store Order {{{{{ Loadの命令の順は守ってくれるモード
RMO: Relaxed Memory Order {{{{{ Load/Storeもどっちの順も守らないモード
メモリモデルがRMOに指定されると、もうプロセッサーのお行儀はひどいものですね。LoadもStoreもしったことかぁっていう感じです。
当然性能は、期待とおり荒くれの順で、RMO > PSO > TSOの順に速くなります。
アウトローの方が行儀は悪いが、やたら速いっていう感じでしょうか? なんとなくイメージと合いますね。^-^
Solaris Operationg Systemは、UltraSPARCをTSOで使用しています。
RMOで走らせたいけど、制御できなくなってしまう‥という状況でしようか。
まだまだUltraSPARCを乗りこなせているとはいえませんねぇ。。。^-^; (やばぁぁぁぁ)
TSOでもloadとstore間で実行の順序を保証しないときがあるので、上の例のような状況が起き得るのです。こういう状況でハングが起きると、もぉ頭が痛いどころではありませんね。(しくしく状況 (;-;))
ちなみに、loadとstore間の実行順序を指定してやりたいときは、membar命令を使ってやります。
上の例ですと、次のようにmembar #StoreLoadを指定してやれば、実行順序はプログラムオーダーのとおりとなります。
CPU1 CPU2
store #1, [A] store #1, [B]
membar #StoreLoad membar #StoreLoad {{{{{ 実際には、この命令が要る
load [B], %r1 load [A], %r2
このmembarを忘れたりすると、いててててぇ状況になってしまったりするんですね。
このmembarについては、/usr/include/sys/atomic.hにコメントが書いてありますので、
近くにSunのシステムがあれば(で、Solarisがインストールしてあれば)見てみて下さいね。
(membar_enter(), membar_exit(), membar_producer(), membar_consumer()のところです)
# OpenSolarisがリリースされれば、堂々とソースを載せられるんですけど。もうしばらくの辛抱ですね。
今日はちょっとイテテの話題でした。仕事柄、こういう痛いトラブルにぶつかることもある‥そんな涙のお話(というより、愚痴かい?)でした。
さて、週末はゆっくり休むことにしましょ。
それでは皆さん、良い週末を
Posted at 06:46午後 5 27, 2005 by eota in opensolaris | Comments[0]
ある失敗談 - Solaris10のdevfsの仕業? それとも‥
先日、テストのお話をしましたよね。今日はその時の失敗談。 実は先日のテスト中、システムでパニックが発生しました。(!) 障害修正のテスト中に、パニックでシステムダウンなんて、「ゲゲッ」っていう状態ですよね。 全く洒落になりませんもの。(^0^) テストでは、お手製のデバイスドライバー(pseudoなやつ)を作って、Solaris DDI関数の動きを調べていたんです。コマンドで調べたい装置のデバイスノードと、そのデバイスの持つregプロパティの番号を指定してやると、お手製ドライバーを通して、Solaris DDI関数の返す値が分かる‥そんな“作り”の予定でした。ところが‥。 カーネルを置き換え、いざテストを開始するとパニック発生です! 「さーっ」と血の気が引いていきます。。。 (いやぁぁぁぁ (T-T) ← こんな気持ち) 落ち込む気分をカフェの一杯のコーヒーで持ち直し(あぁぁ、なぜサンなのにJava coffeeではないのだろうかとか思いつつ)、気を取り直して、クラッシュダンプを調べてみると、ddi_ctlops()の0×20番地で%g1レジスタが0であったためにパニックしたことが分かりました。 ddi_ctlops+0×20: ldx [%g1 + 0×40], %g1 なぜ値が0なのぉ?と更に調べてみると、このドライバーはロードされているものの、まだアタッチされていないために値が0となっていたことが分かりました。 ☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆ ここでアタッチだのロードだの書いてしまったので、ちょっと簡単に解説です。 Solaris Operationg Systemでは、ドライバーは幾つかの状態遷移を経て、(無事)使用できるようになります。 ドライバーを使うには、まずカーネルからアクセスできないといけませんよね。 それには、まず動的にロード(load)してやる必要があります。そうすると、カーネルにモジュールとして登録され、カーネル空間としてアクセス可能になるわけです。 ですが、この段階ではまだドライバーとして機能してません。機能するためには、ドライバーが装置にアタッチ(attach)されないといけません。ドライバーがある装置にアタッチされると、インスタンス番号が割り当てられます。 そしてデバイスドライバーが装置を駆動し‥ハードウェアが使えるようになって‥めでたし、めでたしと、まぁこんな感じに進むわけです。 ☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆ テストの時はドライバーはカーネル空間からみえているものの、実際には装置をまだコントロールしていない、そんな状態だったってことになるんですね。 まぁともかく原因は分かりました。少なくとも僕の修正のせいじゃないな‥。良かったっ! (^-^)v oO (Happiness) そう思うと、ちょっと余裕が生まれてきますよね。 今回も、ちょっとした(余計な?)疑問が頭の中を駆けめぐります。。。 例えば、ddi_ctlops+0×20番地で、なぜ%g1のチェックを入れないんだろう? %g1が0の時だってあるんじゃないのぉ‥Solarisのバカやろぉーとか‥。(^-^; Solaris10にはdevfsというものがあって、装置を使いたいってオープンしにいくと、devfsが勝手にロードしてアタッチしてくれます。余計な‥と思う人もいるでしょうけど、Dynamic ReconfigurationやらPCI Hot Plugなどをサポートするサーバーでは、システム稼働中にPCIカードを引っこ抜いて交換したりするわけですから、そういう場合には、動的にデバイスノードを変更してくれるdevfsの方が都合が良いときもあったりするわけです。 すると、今度は、devfsは僕のドライバーは面倒みてくれないのか‥devfsのバカやろぉーとか(またかいっ)、なるわけでして。(^-^; 更に落ち込む気分をカフェの2杯目のコーヒーで持ち直しつつ考えてみると、そういえば、僕のドライバーでは、調べる装置をオープンしているわけではないなと気づいてしまいました。オープンしているのは、僕のドライバーで、そこから調べる装置の情報を直接取得にいって‥。あぁ、その時に装置をオープンしていないのであれば、アタッチされている保証はないわけだ‥。すると、devfsが悪いわけじゃないなぁ。。。と。 そうなんです。Solaris10では、動的にattach/detachが実行され*やすい*ので、デバイスノードにアクセスしている最中は、holdしておくことが必要なんですね。じゃぁ、どうするのっ? というところで、今度はLayered Driver I/Fに気づきました。Solaris10には、Layered Driver Interface (LDI) というのがあって、ドライバーから*別の*ドライバーをオープンして使うことができるんですね。(うん、ちょっと便利‥かも?) 「そうか、これだぁ、これが必要だったのかぁぁ」 と、気づくわけです。 で、更に落ち込む気分をカフェの3杯目のコーヒーで持ち直しつつ考えてみると、、、 「でも、関係ないじゃんっ?(笑)」 と、ようやく気づくわけです。だって、Solaris DDI関数のテストが目的ですモノね! (^-^) そして、 「このままでは、俺はカフェイン中毒になってしまうではないかっ!」 ということにも‥。 (‥コーヒー飲み過ぎ‥) こんなことをしつつ、僕のサンでの一日がまた過ぎてゆくわけです‥。
Posted at 05:53午後 5 24, 2005 by eota in opensolaris | Comments[0]
mdbでサクサクとカーネルをデバック - mdb (modular debugger)
先日、AMD64のマシンでのハング障害について調べなくてはならなかったのですが、mdbの強力な機能のお陰であまり時間もかからず、助かりました。(^-^)V oO (Yokatta---)
そこで今日はmdbの話をちょっとさせて下さいね。
ハング障害っていうのは、案外と困りもの。だって、パニックだったら、"ここで値が壊れていたのでパニック"っていうのは、大抵は分かるでしょ? (まぁ、どうして値が壊れたの?っていう辺りが実は難しかったりするんですけど、それはちょっとおいて‥)でも、ハングの場合は、どこで(そして、なんで)ハングしてるのぉ?というところから出発しなければいけないんですものね。手間暇がかかるわけです。(~.~;)
でも、mdbには強力な機能があって、どこでハングしているかが簡単に分かります。今日はその方法について、説明しますね。(こういうのは、経験から来るモノですので、貴重な情報ですよぉ。)
ハングした場合は、幾つかケースがあるのですけど、今回はひたすらCPUが走り続ける、端から見ていても暑そうな、そんなケースについて説明しましょう。
(*) タスクがループしているひたすら汗をかいている場合 --- CPUが同じ箇所を永遠に実行し続けてしまう場合です。終了するはずもないので、ハングに見えてしまいますよね。
調査は大抵強制ダンプを採取して行うのですが、この場合はダンプを採取した時に各々のCPUが何を実行していたかをチェックすれば分かります。mdbでは、cpuinfoというコマンドがあって、ダンプが採取された時に各々のCPUで実行されているThreadについて情報を出力してくれます。
まず、ダンプをmdbで見るには、ダンプの番号、0から始まる数字を指定してあげるだけで、unixとvmcoreの双方のデータを読み込みます。例えば、ダンプがunix.0, vmcore.0だとしますよね。そしたら、0という数字を引数で指定してあげるだけです。
% mdb 0 ------- これで、unix.0, vmcore.0を読み込む。
そうすると、色々なメッセージがでてきますが、取りあえずは無視です。(^-^)
mdb: warning: dump is from SunOS 5.10 s10_74l1; dcmds and macros may
not match kernel implementation
Loading modules: [ unix krtld genunix dtrace specfs ufs sd isp sgsbbc
ip sctp wrsm random fcp fctl nca nfs sppp ptm logindmux md wrsmd cpc
fcip ]
そして、この不等号(>)のところでコマンドを打ちましょう。この不等号(>)はコマンドプロンプトなんですね。
コマンドは、コロンを2つつけて書くことになっています。例えば、::dcmdsみたいに。
今回の場合は、cpuinfoを実行することですから、::cpuinfoと打てばいいんですね。で、この時のポイントは、-vオプションをつけてやることです。そうすると、各CPUでの「その時の」threadの状況を出力してくれます。
::cpuinfo -v実はループしていたのは、割り込みレベル4で入っていた、ソフトウェア割り込みだったんですが、threadのスタックは、findstackコマンドで簡単に見れますので、すぐに原因までたどり着くことができました。
ID ADDR FLG NRUN BSPL PRI RNRN KRNRN SWITCH THREAD PROC
1 00001835ac8 1b 1 0 109 no no t-0 2a10007fcc0 sched
| | |
RUNNING --+ | +-- PIL THREAD
READY | 10 2a10007fcc0
EXISTS | - 2a10001fcc0 (idle)
ENABLE |
+-- PRI THREAD PROC
60 2a10165fcc0 sched
ID ADDR FLG NRUN BSPL PRI RNRN KRNRN SWITCH THREAD PROC
3 30000f22000 1d 1 0 -1 no no t-7 2a100255cc0 (idle)
| |
RUNNING --+ +-- PRI THREAD PROC
QUIESCED 60 2a100137cc0 sched
EXISTS
ENABLE
今回の場合は、次のような状態でした。
ID ADDR FLG NRUN BSPL PRI RNRN KRNRN SWITCH THREAD PROC
0 fffffffffbc22960 1f 3 0 109 no no t-5898
fffffe80000b3c80 sched
| | |
RUNNING --+ | +-- PIL THREAD
READY | 10 fffffe80000b3c80 clock interrupt
QUIESCED | 4 fffffe80000b9c80 software interrupt
EXISTS | - fffffe8000005c80 (idle)
ENABLE |
+-- PRI THREAD PROC
99 fffffe80000e9c80 sched
60 fffffe80008b2c80 sched
60 fffffe800017fc80 sched
スタックを調べるには、見てみたいthreadアドレスに::findstackを指定してやるだけです。今回の場合は、怪しいthreadはfffffe80000b9c80ですから、これに::findstackを指定してやります。
fffffe80000b9c80::findstack今回はループしていても、クロック割り込み(レベル10の割り込みthread)が入ってきていましたので、スタックに残っている最後の関数は_resume_from_idle()でしたが、ハングはその前のwait_till_seen()で起きていました。(この関数が、ソフトウェア割り込みで呼び出されることを考慮しておらず、そのために発生したということがこの後すぐに分かりました。)
stack pointer for thread fffffe80000b9c80: fffffe80000b9860
[ fffffe80000b9860 _resume_from_idle+0xde() ]
fffffe80000b9980 wait_till_seen+0x67()
fffffe80000b99e0 remove_av+0xab()
fffffe80000b9a10 rem_avintr+0x52()
fffffe80000b9a40 pci_disable_intr+0x6e()
fffffe80000b9b00 pci_intr_ops+0x467()
fffffe80000b9b40 i_ddi_intr_ops+0xb1()
fffffe80000b9b80 i_ddi_intr_ops+0xb1()
fffffe80000b9b90 i_ddi_handle_intr_ops+9()
fffffe80000b9bc0 ddi_intr_disable+0x52()
fffffe80000b9bf0 bge_reset+0xc7()
fffffe80000b9c10 bge_restart+0x1c()
fffffe80000b9c40 bge_chip_factotum+0xd8()
以前は、mdbのようなデバッカーがなく、adbといったBSD Unixの時代からあるデバッカーを使用していたために、原因判明まで結構な時間がかかったものです。今はmdbがあるから、相当楽になったと思います。^-^
mdbでどのようなコマンドが使えるかは、::dcmdsを実行すると分かります。たくさん出てくるのでびっくりするかも知れませんね。出力された関数のもそっと詳細な情報を知りたいときは、::helpコマンドを使います。
例えば、findstackについてもっと知りたいっていう場合は、::help findstackって叩いてあげます。
::help findstackSolaris Operating Systemのいいところは、こういう強力なデバッカーが標準でついてくるところですね。
NAME
findstack - find kernel thread stack
SYNOPSIS
addr ::findstack [-v]
ATTRIBUTES
Target: kvm
Module: genunix
Interface Stability: Unstable
もちろん、OpenSolaris上でもほとんどすべてのソースが付いてきますので、DTraceやmdbの恩恵を得ることができます。こういう点が、LinuxやNetBSD, FreeBSDに比べて良いところかも知れませんね。開発時のデバックがOpenSolaris上では相当楽になるんじゃないかなって今から期待したいです。(^-^)
それでは良い週末を!
Posted at 03:08午後 5 20, 2005 by eota in opensolaris | Comments[0]
DTrace (Solaris Dynamic Tracing) - 続き
皆さん、こんにちは。週末は如何でしたか? さて、先日DTraceのanonymous tracingについてお話しましたけれど、hoge.dを作った後、それをどうやってシステムに登録するか、全然説明がなかったですねぇ。(^-^;) 実は、次のように-AFsを指定してdtraceコマンドに食べさせてあげるだけなんです。 # dtrace -AFs hoge.d (hoge.dの登録) これで、自分で作ったDTaceスクリプトがブート時に実行されるってことですね。 解除するには、-Aを指定するだけですよ。 # dtrace -A (hoge.dの解除) このDTraceスクリプト、なにげに便利です。今日はもそっと説明しましょうね。 Unixのlsコマンドって、結構使いますよね。何かあると、無意識に"ls" バッシ!って叩いちゃう。でも、このlsコマンドを実行すると、システムでは何が起きているのだろう‥って時々気になりません? DTraceスクリプトを使うと、簡単に分かっちゃうんですよ。 まず、次のスクリプトを用意してあげます。 #!/usr/sbin/dtrace -s syscall:::entry /execname == "ls"/ { self->traceme++; } syscall:::return /execname == "ls"/ { self->traceme--; } fbt::: /self->traceme/ {} 簡単に説明すると、システムコールが発行されたとき、プログラムが"ls"だったらフラグを立てます。そのシステムコールが終了するときに、フラグを落とすので、"ls"がシステムに要求を出している間はフラグが立っていることになりますね。この処理が最初の2つ、syscall:::entryとsyscall:::returnのところですね。 そして、フラグが立っている間、カーネルの関数をトレースしてやります。それが3つ目のfbt:::のところです。 こんなんで"ls"のために働く関数が全部分かっちゃうわけです。簡単、簡単。 それでは実行してみましょうね。上のファイルをls.dとして保存して、実行権をつけてあげます。 そしてスーパユーザで実行してあげましょう。 # ./ls.d dtrace: script './ls.d' matched 34488 probes dtraceほんちゃら‥ってメッセージがでますけれど、これは無事dtraceが動いている証拠。 そして別のウインドウで"ls"を実行してあげます。 # ls / すると、あら不思議、dtraceを実行したウインドウから次々とカーネルの関数が出力されます。 : 0 1483 page_set_props:entry 0 1484 page_set_props:return 0 1633 page_io_lock:entry 0 1634 page_io_lock:return 0 2642 page_add:entry 0 2643 page_add:return 0 3676 page_add_common:entry 0 3677 page_add_common:return 0 3551 page_create_va:return : 0 11178 syscall_mstate:entry 0 11179 syscall_mstate:return 0 11178 syscall_mstate:entry 0 11179 syscall_mstate:return 0 11178 syscall_mstate:entry 0 11179 syscall_mstate:return プログラムを停止するには、^Cを叩きます。 0 11179 syscall_mstate:return ^C # でも、こんなにもたくさんの関数が、なにげない"ls"コマンドのために働いていたんですね。 なかなか働き者。よしよしとたまには労ってあげましょうね。^-^ それでは
Posted at 12:20午後 5 17, 2005 by eota in opensolaris | Comments[0]
DTrace (Solaris Dynamic Tracing) - powerful tracer on Solaris10
皆さん、DTrace (Solaris Dynamic Tracing)ってご存知ですか? Solaris10で導入されたトレース機能です。これって本当に便利なんですよ。 カーネルのデバックでもかなり活躍します。欠点といえば、ソースがないとフックする関数であるとか、引数であるとか分かりにくい(ヘッダーファイルをみればある程度想像できますけど)こととかですが、これはあのOpenSolarisがリリースされれば、解決しますね! ^-^ DTraceにはanonymous tracingというのがあって、カーネルブート中にも自動でDTraceを起動して、問題を追跡することができます。この前のテストの時に、とあるカーネルモジュールからワーニングが出るようになって、それが僕の修正のせいなのかどうか、調べる必要がでてきました。 そこで、このanonymous tracingの出動ですよ、皆さん! 方法は至って簡単。 まずは追跡するモジュール名を調べます。ここではhogeとしましょうね。 そしたら、次のコマンドをスーパーユーザで実行するだけ。 # dtrace -A -m hoge そうすると、dtraceがフックを貼ったことを教えるメッセージがドワーと出ますので、取りあえず無視。そして、rebootです。 # reboot そうすると、次のブートの時にdtraceが起動されて、動いたことを示すメッセージがブート中に出ます。それを見てよしよしと納得しましょう。うまく行っている証拠。^o^ 起動したら、dtraceを叩いて、早速ログを拝見。 # dtrace -a このログを見て更に絞り込み。例えば、hogeモジュールのhogehoge関数が怪しいと睨めば、次のような簡単なスクリプトを作るだけ。 hoge.d fbt::hogehoge:entry { self->trace = 1; } fbt::: /self->trace/ { } fbt::hogehoge:return { self->trace = 0; } これは、hogehoge関数が呼び出されたら、トレースをスタートさせ、呼び出しが終了したらトレースを停止させるというものです。簡単だぁ。 これを作ったのはBryan Cantrillさんです。もう感謝、感謝。謝謝の世界ですね。 で、僕も早速やってみました。ところが‥、何もトレースがとれないではありませんかっ! 何でぇ‥と調べてみると、ブート時にプラットフォーム依存で呼び出されるモジュールで、dtraceモジュールより先にロードされてしまっていました。僕の調べたかったのは、そのモジュールのattachルーチンなので、ロードされる時に呼び出されてしまうのですよね。^.^; dtraceモジュールは/etc/systemでforceloadされているんですけど、ブート時に必要な幾つかのモジュールはそれより先にロードされてしまうのがあるんです。トラブルのものはそんな内の1つでした。あぁぁぁ。 結局、kmdbを起動。昔ながらのkernelデバッカを使用して調査です。。。寂しい。 ちなみにkmdbは、Solaris9で提供されていたmdbのカーネル用です。従来のkadbの置き換えなんですが、こっちも相当に便利っ! でも、そのお話はまた別の機会に。。。 えっ、調査結果? 結局、ワーニングは僕の修正とは関係がないことが分かりました。 めでたし、めでたし‥かな? おっと、最後にanonymous tracingを止める方法を。これはマニュアルにも載っていなかったので、調べるのにちょっと時間がかかりました。^-^; # dtrace -A って実行するだけです。これでフックが全部外れて、ブート時のトレースを停止します。これもシンプルですね。
Posted at 12:15午後 5 12, 2005 by eota in opensolaris | Comments[1]
Blackout on Screen --- OBP setting
今日は先日出くわした本当のお話。 障害修正で、あるグラフィックスカードが必要になったので、同僚から借りてテストスタートです。 僕の愛機Sun Blade 2500にPCIカードをブスと差し、よしよしと‥。 以前と比べて、今のWSやPCはこういう周辺装置の装着が楽になりましたね。以前は一汗書いたモノですけど、今は"作り"も考えられているので。蓋もドライバ無しで外せますし。 SBusカードも楽でしたけど、ここまで何も考えずにできるのでは、あまり不満はないです。はい。 ご苦労さまです > HWの方。 と、まぁここまではススーイと、いい感じ。ですが、その後でした。。。。 コンソールを新しいグラフィックスカードに切り替える必要があるんですよね。それを使うには。 そう、ここが何となくイヤなところ‥そして本当にイヤなことがぁぁ。 OBPではdevice aliasでscreenが最初からきられています。で、普通これがoutput-device、つまりはコンソールになるわけなんですね。例えば、こんな風。 {0} ok printenv output-device output-device = screen {0} ok devalias screen screen /pci@1f,700000/SUNW,XVR-100@2 そして、このoutput-deviceなり、screenなりを新しいグラフィックスカードに変えるわけです。 僕は、screenの方を新しく定義し直しました。(こういう時はnvdevaliasを使います) こんな感じ。 {0} ok nvdevalias screen /pci@1f,600000/SUNW,XVR-600@3 が、しかし‥、こともあろうにタイポ。どこか間違えたみたいで、reset-allの後いきなり真っ黒。 (皆さんは、ちゃんと確認してからリセットしましょう!) さぁ大変です。何にも見えない、分からない。。。。。でもキーボードは使えるみたい。 困ったときのL1+Aも動くみたい。reset-allを実行すると、確実にリセットされます(って ^-^;) ここは冷静にL1+Nを実行しました。リセット後にL1+Nを押下し続けるんです。そうすると、デフォルトの設定に戻る‥はずだったのに、、、画面は真っ黒のまんま。 どうやら、L1+Nはscreenが定義されていると、新たにscreenは設定しないみたい。予想外だった。 ここで焦ります。。。ここは僕の必殺メクラ打ち。だめだぁぁー。。あぁぁどうしよう。。 この時、ふと側にシリアルケーブル(クロスです)があることに気づきました。これっだっ! 神様ありがとう! 僕を見捨てていなかったのですね! (^-^) 早速側のPCにケーブルをつなぎます。PCのシリアルポートAとBlade2500のシリアルポートBを接続。 その後、グラフィックスケーブルとキーボードを外します。そしてBlade2500をリセット。 続いて、PC上のSolaris Operating System(そう、Solaris X86だったんですよ)上で、次を実行する。 % tip hardwire これでBlade2500のコンソールがシリアルポート経由で接続されました。あー、便利。 これで画面が復活です。 調べてみると、何のことはない、screenが次のようになってました。やっぱメクラ打ち、どっか間違えていると思ったら、、、どこかのバイクの名前のようになってました。("-";) screen /pci@1f,700000/SUNW,ZVR-100@2 カリフォルニアの風にさそわれて、バイクでも乗りにいきたいなぁ。。。 という私はバイクは持っていません(免許もない)。はぁぁぁ。テスト開始しました。。
Posted at 12:02午後 5 11, 2005 by eota in opensolaris | Comments[0]
皆さん、こんにちは。
☆☆☆ ついに僕もブログを始めることになりました‥。しかも、こんなところから。思えば、カーネルエンジニアに憧れることうん十年、まさか今サンにお勤めだなんて‥嬉しい‥。 ☆☆☆ まずは、簡単な自己紹介から。僕=ソフトエンジニア屋さん。シリコンバレーに生息しています。 サンでは、Solaris Operating SystemのカーネルI/O周りをやっています。 割り込みやらI/OメモリやらD(V)MAやら、あっちをつまみ、こっちをつまみって感じで。 I/Oのパフォーマンスをあげるべく、悩める日々をおくっています。 かれこれサンとは3年ぐらいのおつき合いになりました。自由な社風があるサンはとっても好きです。 OpenSolarisがリリースされたら、色んな人たちとたくさんのことをしたいって今思っています
Posted at 04:27午後 5 10, 2005 by eota in life | Comments[0]
