|
|
1.1 root 1: //
2: // nono
1.1.1.3 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
1.1.1.2 root 7: #include "console.h"
8: #include "debugger_private.h"
9: #include "debugger_m680x0.h"
10: #include "debugger_m88xx0.h"
11: #include "mainapp.h"
1.1.1.3 root 12: #include "mythread.h"
1.1.1.7 ! root 13: #include "scheduler.h"
1.1.1.3 root 14: #include <algorithm>
1.1 root 15: #include <csignal>
16:
1.1.1.3 root 17: static Debugger *gDebugger;
1.1.1.4 root 18: BreakpointMonitor *gBreakpointMonitor;
1.1 root 19: CVPrompt *gCVPrompt;
20:
21: static void *debugger_run(void *);
22:
23: // 初期化。
24: // この時点で VM が初期化されていること。
25: void
26: debugger_init()
27: {
28: gDebugger = new Debugger();
29: gDebugger->Init();
30:
1.1.1.4 root 31: gBreakpointMonitor = new BreakpointMonitor();
1.1 root 32: gCVPrompt = new CVPrompt();
33:
34: // デバッガスレッド起動
35: pthread_t th;
36: pthread_create(&th, NULL, debugger_run, NULL);
37: }
38:
39: // デバッガスレッドのエントリポイント
40: static void *
41: debugger_run(void *dummy)
42: {
1.1.1.3 root 43: PTHREAD_SETNAME("Debugger");
1.1 root 44: pthread_detach(pthread_self());
45:
46: gDebugger->ThreadRun();
47: return NULL;
48: }
49:
50: // コンストラクタ
51: Debugger::Debugger()
52: {
1.1.1.2 root 53: if (gMPU680x0) {
54: md = new DebuggerMD_m680x0(this, gMPU680x0->GetCPU());
55: } else if (gMPU88xx0) {
56: md = new DebuggerMD_m88xx0(this, gMPU88xx0->GetCPU());
57: } else {
58: throw "unknown mpu";
59: }
1.1 root 60: }
61:
62: // コマンドライン引数によって動作を決めるところ。
63: // 名前がこれでいいのかはあるけど。
64: void
65: Debugger::Init()
66: {
67: // -d なら CPU 起動時点で停止してプロンプトを待つ
1.1.1.2 root 68: if (gMainApp.debug_on_start) {
69: md->ReqSet(CPU_REQ_PROMPT);
1.1 root 70: }
71:
72: // -b <addr> ならブレークポイント設定
1.1.1.4 root 73: for (auto& str : gMainApp.debug_breakaddr) {
74: breakpoint_t bp;
75:
1.1.1.2 root 76: md->ReqSet(CPU_REQ_TRACE);
1.1.1.4 root 77:
78: // <addr>,<skip> で分離できたら <skip> を取り出す
79: int pos = str.find(',');
80: if (pos != std::string::npos) {
81: std::string skipstr = str.substr(pos + 1);
82: bp.skip = atoi(skipstr.c_str());
83:
84: // XXX これ出来るんだっけ?
85: str[pos] = '\0';
86: }
87: // そしてどちらにしても <addr> を取り出す
88: if (!ParseAddr(str.c_str(), &bp.addr)) {
89: warnx("\"%s\": Invalid breakpoint address", str.c_str());
90: return;
91: }
92: // 登録
93: bp.type = BreakpointType::Address;
94: AddBreakpoint(bp);
1.1 root 95: }
96:
97: // どこでやるべか
98: signal(SIGPIPE, SIG_IGN);
99: }
100:
101: // デバッガスレッド
102: void
103: Debugger::ThreadRun()
104: {
105: // XXX どこかでコンソールの選択とパラメータの取得
1.1.1.2 root 106: if (gMainApp.debug_on_console) {
107: cons = new ConsoleStdio();
108: } else {
109: cons = new ConsoleTCP();
110: }
1.1 root 111: if (cons->Init() == false) {
1.1.1.3 root 112: delete cons;
1.1 root 113: return;
114: }
115:
1.1.1.5 root 116: // disp_regs の初期値設定。
117: // 再接続でも継続してていいような気がするのでループ外で初期化。
118: disp_regs.clear();
119: disp_regs.push_back("r");
120:
1.1 root 121: for (;;) {
1.1.1.3 root 122: // 着信待ち
123: if (Accept() == false) {
124: break;
125: }
126:
1.1.1.2 root 127: cons->InitEditLine("> ");
1.1 root 128:
1.1.1.3 root 129: // デバッガプロンプトにいる間はトレースオン
130: md->ReqSet(CPU_REQ_TRACE);
131:
132: bool first = true;
133: for (;;) {
134: // 接続後の1回目だけ実行するもの。
135: if (first) {
136: first = false;
137:
138: // greeting はプロンプトが取れる前にもう表示したい。
139: // 何らかの事故でプロンプトが取れなくても、ここまでは接続
140: // できてることが分かるように。
141: cons->Print("This is debugger console\n");
142:
143: // 接続ごとに初期化する値
144: n_enable = false;
145: s_enable = false;
1.1.1.5 root 146: t_enable = true;
1.1.1.3 root 147: }
148:
149: // プロンプトが取れるのを待つ
150: if (AcquirePrompt() == false) {
151: break;
152: }
153:
1.1.1.6 root 154: // d/m をいきなり引数なしで実行した時のため、現在地にしておく。
1.1.1.3 root 155: pc = md->GetPC();
1.1.1.6 root 156: m_last_addr.Set(pc, md->IsSuper(), true);
157: d_last_addr.Set(pc, md->IsSuper(), false);
158: // プロンプトに来た時に表示するいつものやつ
1.1.1.3 root 159: cmd_minus();
160:
161: // コマンド処理のメインループ
162: auto action = MainLoop();
163:
164: // 次回との差分のため、今のレジスタセットをバックアップ
165: md->BackupRegs();
166:
167: // メインループから戻ったということは Leave か Quit なので
168: // どちらにしても、ここでプロンプトを手放す。
169: gCVPrompt->NotifyRelease();
170:
171: // quit ならここでループを1段抜ける
172: if (action == CommandAction::Quit) {
173: break;
174: }
175: }
176:
177: // デバッガを抜けるのでトレースオフ
1.1.1.2 root 178: md->ReqClr(CPU_REQ_TRACE);
1.1 root 179:
180: cons->Close();
1.1.1.3 root 181: if (gMainApp.debug_on_console) {
182: break;
183: }
1.1 root 184: }
185:
186: delete cons;
187: }
188:
1.1.1.3 root 189: // コンソールに接続されるのを待つ。
190: // うーん、なんだこれ。
191: //
192: // +--- debug_on_console (-D オプション)
193: // | +- debug_on_start (-d オプション)
194: // | | 動作
195: // 0 0 TCP、Accept で待機。
196: // 0 1 TCP、Accpet で待機。
197: // 1 0 stdio、起動時にプロンプトで止まらない -> キー入力待ち。
198: // 1 1 stdio、起動時にプロンプトで止まる。
1.1 root 199: bool
1.1.1.3 root 200: Debugger::Accept()
201: {
202: if (gMainApp.debug_on_console == false) {
203: // TCP なら accept が成功するのを待つ
204: cons->Accept();
205: } else {
206: if (gMainApp.debug_on_start == false) {
207: // stdio、起動時にプロンプトで止まらない。
208: // この場合キー入力かブレークポイントなどで止まった時点で
209: // プロンプトを出したいのでちょっと面倒なことに。
210: for (;;) {
211: bool is_prompt = gCVPrompt->WaitAcquire(200);
212: if (is_prompt) {
213: break;
214: }
215:
216: int r = cons->Poll(200);
217: if (r < 0) {
218: cmd_q();
219: return false;
220: }
221: if (r > 0) {
222: break;
223: }
224: }
225: } else {
226: // stdio、起動時にプロンプトで止まる。
227: // この場合は無条件に成功でよい。
228: }
229: }
230:
231: return true;
232: }
233:
234: // プロンプトが取れるのを待つ
235: // 失敗すれば false を返す
236: bool
237: Debugger::AcquirePrompt()
1.1 root 238: {
1.1.1.3 root 239: md->ReqSet(CPU_REQ_PROMPT);
240:
1.1.1.2 root 241: for (;;) {
242: bool is_prompt = gCVPrompt->WaitAcquire(200);
1.1.1.3 root 243: if (is_prompt)
244: break;
1.1.1.2 root 245:
246: // コンソールを定期観測して、入力があればブレーク要求
247: if (cons->Poll()) {
248: // この入力は drop してみる
249: if (cons->Gets(cmdbuf) == false) {
250: // EOF or Error
251: cmd_q();
252: return false;
253: }
254: md->ReqSet(CPU_REQ_PROMPT);
255: }
256: }
1.1.1.3 root 257: return true;
258: }
1.1 root 259:
1.1.1.3 root 260: // コマンド処理のメインループ。
261: // コマンドが Stay ならこの中で処理を継続。
262: // コマンドが Leave か Quit ならその値を持って戻る (呼び出し側で処理する)。
263: Debugger::CommandAction
264: Debugger::MainLoop()
265: {
266: for (;;) {
1.1.1.7 ! root 267: // ブレークポイント到達メッセージがあればここで表示
! 268: if (bpointmsg.empty() == false) {
! 269: cons->Print("%s", bpointmsg.c_str());
! 270: bpointmsg.clear();
! 271: }
! 272:
1.1.1.3 root 273: cons->Prompt();
274: if (cons->Gets(cmdbuf) == false) {
275: // EOF or Error
276: return CommandAction::Quit;
277: }
1.1 root 278:
1.1.1.3 root 279: string_rtrim(cmdbuf);
1.1 root 280:
1.1.1.3 root 281: if (!cmdbuf.empty()) {
282: // コマンドが入力されれば次回のために保存
283: last_cmdbuf = cmdbuf;
284: } else {
285: // 空行が入力されれば直前のコマンドをもう一度
286: // 前行がなければ何もせずもう一度プロンプトを表示するかね
287: if (last_cmdbuf.empty())
288: continue;
289: cmdbuf = last_cmdbuf;
290: }
291:
292: // 行を args に分解
293: ParseCmdbuf();
294:
295: // 前回の値が有効なのはコマンドが連続した時だけ
296:
297: // コマンドをテーブルから探す
298: auto it = std::find_if(cmdtable.begin(), cmdtable.end(),
299: [=](auto x) { return strcmp(args[0].c_str(), x.name) == 0; });
300: if (it != cmdtable.end()) {
301: auto cmd = *it;
302:
303: // 見付かれば実行
304: (this->*(cmd.func))();
305:
306: // Stay なら引き続きコマンド処理
307: if (cmd.action == CommandAction::Stay) {
308: continue;
309: }
310: // それ以外ならメインループ終了
311: return cmd.action;
312: }
1.1 root 313:
1.1.1.3 root 314: // (コマンドテーブルになくて) "r" から始まっていればレジスタ表示系
315: if (args[0][0] == 'r') {
316: // ShowRegister() は処理したら true を返す。
317: // "r" 系コマンドはすべて Stay。
318: if (md->ShowRegister(cons, args)) {
319: continue;
320: }
321: }
1.1.1.2 root 322:
1.1.1.3 root 323: // 知らないコマンドも Stay 相当。
324: cons->Print("%s: unknown command\n", args[0].c_str());
325: continue;
326: }
327: __unreachable();
1.1 root 328: }
329:
330: // ブレークポイントとかを調べる。
331: // CPU_REQ_TRACE フラグが立っている間1命令ごとに呼び出される。
332: bool
333: debugger_check()
334: {
335: return gDebugger->Check();
336: }
337:
338: // デバッガ実行中なら命令開始前にメインルーチンから呼ばれる。
339: // プロンプトに降りるなら true を返す。
340: bool
341: Debugger::Check()
342: {
1.1.1.5 root 343: bool is_break = false;
344:
345: // ブレークポイントは他のチェックとは併用になるので先に調べる。
346: if (CheckAllBreakpoints()) {
347: is_break = true;
348: }
349: // アドレス指定付き continue もブレークポイントと似た動作なのでこっち。
350: // XXX ブレークポイントとして実装するかどうか
351: if (bc_enable) {
352: if (bc_addr == md->GetPC()) {
353: bc_enable = false;
354: is_break = true;
1.1 root 355: }
356: }
357:
1.1.1.5 root 358: // XXX 残りは排他動作のはず
359:
360: // いずれの場合も、ステップ実行が終了条件にマッチしたら、ブレークポイント
361: // の成否に関わらず true を返せばいい。
362: // 終了条件が来ないうちに先にブレークポイントに到達した場合でも、ブレーク
363: // ポイントによりプロンプトに降りるわけなので、実行中のステップ実行を
364: // キャンセルする。この場合もブレークポイント側が true なので true を
365: // 返せばよい。
366:
367: // t (トレース表示) が enable なら、終了条件にマッチしなくてもここで
368: // レジスタを表示。終了条件にマッチする場合に表示するとプロンプトで
369: // もう一回表示されて二重になってしまうので注意。
370:
371: if (s_enable) { // ステップ実行が..
1.1 root 372: s_count--;
1.1.1.5 root 373: if (s_count == 0) { // 成立
374: s_enable = false;
375: return true;
376: } else if (is_break) { // 非成立だがブレークが成立
1.1 root 377: s_enable = false;
378: return true;
1.1.1.5 root 379: } else if (t_enable) { // どちらも非成立で trace on
380: cmd_minus();
1.1 root 381: }
382:
1.1.1.5 root 383: } else if (so_enable) { // ステップアウトが..
384: if (md->IsStepOut()) { // 成立
1.1 root 385: so_enable = false;
386: return true;
1.1.1.5 root 387: } else if (is_break) { // 非成立だがブレークが成立
388: so_enable = false;
389: return true;
390: } else if (t_enable) { // どちらも非成立で trace on
391: cmd_minus();
1.1 root 392: }
393:
1.1.1.5 root 394: } else if (n_enable) { // 次命令まで実行が..
395: if (n_breakaddr != 0xffffffff) {
396: // ステップインをスキップ中
397: if (n_breakaddr == md->GetPC()) {
398: n_count--;
399: }
400: } else {
401: // 1命令実行
402: n_count--;
403: }
404: if (n_count == 0) { // 成立
405: n_enable = false;
406: return true;
407: } else if (is_break) { // 非成立だがブレークが成立
408: n_enable = false;
1.1 root 409: return true;
410: }
1.1.1.5 root 411: if (t_enable) { // どちらも非成立で trace on
412: cmd_minus();
413: }
414:
415: // スキップ中でなければ、ステップインが起きるか都度調べる。
416: // すでにスキップ中なら到達するまでは何もしない。
417: if (n_breakaddr == 0xffffffff) {
418: SetNBreakpoint();
419: }
1.1 root 420: }
421:
1.1.1.5 root 422: // ステップ実行系がなければブレークポイントの成否だけ
423: return is_break;
1.1 root 424: }
425:
1.1.1.4 root 426: // ブレークポイントがどれかでも成立するかを調べる。
427: // 1つ以上成立してブレークするなら true を返す。
428: // 1つも成立しておらずブレークしないなら false を返す。
1.1.1.7 ! root 429: // このルーチン内では Console があることを期待してはいけない (プロンプトが
! 430: // ない時でも呼ばれるので)。
1.1.1.4 root 431: bool
432: Debugger::CheckAllBreakpoints()
433: {
434: bool is_break = false;
435:
436: // 命令ごとにクリアする
437: bi_inst = 0;
438: bi_inst_bytes = 0;
439:
440: // 1つの条件でマッチしても(そこでブレークすること自体は確定するのだが)
441: // 残りの他の条件も成立すればカウントを進める必要があるため、
442: // 全部処理した上でどれか一つでもブレークしたかで判断する必要がある。
1.1.1.5 root 443: for (int i = 0, end = bpoint.size(); i < end; i++) {
444: auto& bp = bpoint[i];
445:
1.1.1.4 root 446: switch (bp.type) {
447: case BreakpointType::Address:
448: if (bp.addr != md->GetPC()) {
449: continue;
450: }
451: break;
452:
453: case BreakpointType::Memory:
454: if (!md->CheckLEA(bp.addr)) {
455: continue;
456: }
457: break;
458:
459: case BreakpointType::Exception:
460: if (bv_vector >= 0) {
461: if (bp.vec1 <= bv_vector && bv_vector <= bp.vec2) {
462: break;
463: }
464: }
465: continue;
466:
467: case BreakpointType::Instruction:
468: if (CheckBreakpointInst(bp) == false) {
469: continue;
470: }
471: break;
472:
473: default:
474: continue;
475: }
476:
477: // 条件は成立したのでカウントとスキップ
478: bp.matched++;
479:
480: // skip == -1 は常にブレークしない (カウントするだけ)
481: if (bp.skip < 0) {
482: continue;
483: }
484:
485: // skip が 1 以上なら、remain を減算
486: if (bp.skip > 0 && --bp.skipremain > 0) {
487: continue;
488: }
489:
490: // ブレーク成立
491: is_break = true;
1.1.1.5 root 492:
493: std::string desc;
494: switch (bp.type) {
495: case BreakpointType::Address:
496: desc = string_format("addr $%08x", bp.addr);
497: break;
498: case BreakpointType::Memory:
499: desc = string_format("mem $%08x", bp.addr);
500: break;
501: case BreakpointType::Exception:
502: {
503: desc = string_format("excp $%02x", bv_vector);
504: const char *name = md->GetExceptionName(bv_vector);
505: if (name != NULL && name[0] != '\0') {
506: desc += string_format(" \"%s\"", name);
507: }
508: break;
509: }
510: case BreakpointType::Instruction:
511: desc = string_format("inst %0*x",
512: bi_inst_bytes * 2,
513: bi_inst >> ((4 - bi_inst_bytes) * 8));
514: break;
515: default:
516: assert(false);
517: break;
518: }
1.1.1.7 ! root 519:
! 520: // 到達メッセージを作成。
! 521: // この時点ではまだコンソールを取得していない可能性があるので
! 522: // (-D なしで起動した場合とか)、表示せず用意するだけ。
! 523: // コンソールが取得できたところで表示する。
! 524: bpointmsg += string_format("breakpoint #%d (%s) reached\n",
! 525: i, desc.c_str());
1.1.1.4 root 526: }
527:
528: // 例外通知は通過ごとに常に下ろしておく
529: bv_vector = -1;
530:
531: // ブレークポイントが1つでも成立したかどうかを返す
532: return is_break;
533: }
534:
535: // 命令ブレークポイントが成立するか調べる。
536: bool
537: Debugger::CheckBreakpointInst(breakpoint_t& bp)
538: {
539: // uint32 bi_inst が現在の PC 位置の命令データ (左詰め)
540: // bi_inst_bytes が読み込んだバイト数(bi_inst の左からの有効バイト数)。
541: // bi_need_bytes が現在のブレークポイントで読み込む必要のあるバイト数。
542:
1.1.1.6 root 543: saddr_t laddr(md->GetPC(), md->IsSuper());
544: DebuggerMemoryStream mem(md, laddr);
1.1.1.5 root 545:
1.1.1.4 root 546: // 必要なバイト数に達するまで読み足す
1.1.1.5 root 547: bi_inst = 0;
1.1.1.4 root 548: while (bi_inst_bytes < bi_need_bytes) {
1.1.1.6 root 549: uint64 data = mem.FetchInst();
1.1.1.5 root 550: if ((int64)data < 0) {
551: return false;
1.1.1.4 root 552: }
553:
1.1.1.5 root 554: bi_inst <<= md->inst_bytes * 8;
555: bi_inst |= data;
556: bi_inst_bytes += md->inst_bytes;
1.1.1.4 root 557: }
1.1.1.5 root 558:
1.1.1.4 root 559: // 左詰めにする
560: bi_inst <<= (4 - bi_inst_bytes) * 8;
561:
562: // 必要なバイト数取得できたので比較
563: if ((bi_inst & bp.mask) == bp.inst) {
564: return true;
565: }
566: return false;
567: }
568:
569: // 例外通知 (MPU からの連絡用)
570: void
1.1.1.5 root 571: debugger_notify_exception(int vector)
1.1.1.4 root 572: {
573: gDebugger->NotifyException(vector);
574: }
575:
576: // 例外通知 (本体)
577: void
578: Debugger::NotifyException(int vector)
579: {
580: bv_vector = vector;
581: }
582:
1.1 root 583: // コマンド一覧。
1.1.1.3 root 584: // "r" から始まるレジスタ表示系コマンドはコード中で別処理してある。
585: /*static*/ std::vector<Debugger::cmddef_t> Debugger::cmdtable = {
1.1.1.4 root 586: { "bi", &Debugger::cmd_bi, Debugger::CommandAction::Stay },
587: { "bm", &Debugger::cmd_bm, Debugger::CommandAction::Stay },
588: { "bv", &Debugger::cmd_bv, Debugger::CommandAction::Stay },
1.1.1.3 root 589: { "bx", &Debugger::cmd_bx, Debugger::CommandAction::Stay },
590: { "b", &Debugger::cmd_b, Debugger::CommandAction::Stay },
591: { "brhist", &Debugger::cmd_brhist, Debugger::CommandAction::Stay },
592: { "c", &Debugger::cmd_c, Debugger::CommandAction::Leave },
593: { "d", &Debugger::cmd_d, Debugger::CommandAction::Stay },
594: { "dt", &Debugger::cmd_dt, Debugger::CommandAction::Stay },
595: { "D", &Debugger::cmd_D, Debugger::CommandAction::Stay },
1.1.1.5 root 596: { "disp", &Debugger::cmd_disp, Debugger::CommandAction::Stay },
1.1.1.4 root 597: { "exhist", &Debugger::cmd_exhist, Debugger::CommandAction::Stay },
1.1.1.5 root 598: { "hb", &Debugger::cmd_hb, Debugger::CommandAction::Stay },
599: { "hr", &Debugger::cmd_hr, Debugger::CommandAction::Stay },
1.1.1.3 root 600: { "h", &Debugger::cmd_h, Debugger::CommandAction::Stay },
601: { "help", &Debugger::cmd_h, Debugger::CommandAction::Stay },
602: { "L", &Debugger::cmd_L, Debugger::CommandAction::Stay },
603: { "m", &Debugger::cmd_m, Debugger::CommandAction::Stay },
604: { "mt", &Debugger::cmd_mt, Debugger::CommandAction::Stay },
605: { "M", &Debugger::cmd_M, Debugger::CommandAction::Stay },
606: { "n", &Debugger::cmd_n, Debugger::CommandAction::Leave },
1.1.1.5 root 607: { "nt", &Debugger::cmd_nt, Debugger::CommandAction::Leave },
1.1.1.3 root 608: { "q", &Debugger::cmd_q, Debugger::CommandAction::Quit },
609: { "quit", &Debugger::cmd_q, Debugger::CommandAction::Quit },
1.1.1.5 root 610: { "reset", &Debugger::cmd_reset, Debugger::CommandAction::Leave },
1.1.1.3 root 611: { "s", &Debugger::cmd_s, Debugger::CommandAction::Leave },
1.1.1.5 root 612: { "st", &Debugger::cmd_st, Debugger::CommandAction::Leave },
1.1.1.3 root 613: { "so", &Debugger::cmd_so, Debugger::CommandAction::Leave },
1.1.1.5 root 614: { "sot", &Debugger::cmd_sot, Debugger::CommandAction::Leave },
1.1.1.3 root 615: { "show", &Debugger::cmd_show, Debugger::CommandAction::Stay },
616: { "t", &Debugger::cmd_t, Debugger::CommandAction::Leave },
617: { "-", &Debugger::cmd_minus, Debugger::CommandAction::Stay },
1.1 root 618: };
619:
620: // ヘルプ
1.1.1.5 root 621: // h : 一覧を表示
622: // h <cmd> : 単独コマンドの詳細を表示
1.1 root 623: void
624: Debugger::cmd_h()
625: {
1.1.1.5 root 626: if (args.size() < 2) {
627: // 引数なしなら一覧表示。
1.1.1.6 root 628: ShowHelpList(HelpListMain);
1.1.1.5 root 629: return;
1.1.1.3 root 630: }
1.1.1.5 root 631:
632: // 引数があれば個別の詳細
633: bool try_again = false;
634: std::string cmd = args[1];
635: do {
1.1.1.6 root 636: // レジスタ系のヘルプを MD から取得して、ローカル変数で足す
637: auto details = HelpDetails;
638: for (const auto& dict : md->GetHelpReg()) {
639: details.push_back(dict);
640: }
1.1.1.5 root 641:
1.1.1.6 root 642: // そこから検索
643: for (const auto& dict : details) {
1.1.1.5 root 644: if (cmd == dict.first) {
645: const auto& desc = dict.second;
646: if (desc[0] == '=') {
647: // "=<cmd>" 形式なら <cmd> を探しなおす。
648: // 別名で同じヘルプを指すシンボリックリンクみたいなもの。
649: cmd = desc.substr(1);
650: try_again = true;
651: break;
652: }
653: // そうでなければこれを表示して終了
654: std::string disp = HelpConvert(dict.second);
655: cons->Print("%s", disp.c_str());
656: return;
657: }
658: }
659: } while (try_again);
660: cons->Print("invalid command name: %s\n", args[1].c_str());
1.1.1.3 root 661: }
662:
1.1.1.5 root 663: // hb : ブレークポイント系コマンドの一覧を表示
664: // こいつだけ結構占めるので別階層。
665: void
666: Debugger::cmd_hb()
667: {
1.1.1.6 root 668: ShowHelpList(HelpListBreakpoints);
1.1.1.5 root 669: }
670:
671: // hr : レジスタ表示系コマンドの一覧を表示
672: // CPU ごとに違うので。
673: void
674: Debugger::cmd_hr()
675: {
1.1.1.6 root 676: ShowHelpList(md->GetHelpListReg());
1.1.1.5 root 677: }
1.1.1.3 root 678:
1.1.1.6 root 679: // ヘルプ一覧を表示。
1.1.1.3 root 680: void
1.1.1.6 root 681: Debugger::ShowHelpList(const HelpMessages& msgs)
1.1.1.3 root 682: {
1.1.1.5 root 683: cons->Print("Type \"help <cmd>\" for indivisual details.\n");
1.1.1.3 root 684: for (const auto& pair : msgs) {
1.1.1.5 root 685: cons->Print(" %-16s %s\n", pair.first.c_str(), pair.second.c_str());
1.1 root 686: }
687: }
688:
1.1.1.5 root 689: // 個別ヘルプメッセージを出力用に置換。
690: // 1. 行頭の連続する改行 (通常は1つのはず) を取り除く。
691: // 2. 末尾の連続するタブ (通常は1つのはず) を取り除く。
692: // 3. (各行頭の) タブを空白4つに置換する。
693: std::string
694: Debugger::HelpConvert(const std::string& src)
695: {
696: int start = 0;
697: int len = src.size();
698:
699: // 末尾の連続するタブをカウント
700: while (src[len - 1] == '\t') {
701: len--;
702: }
703: // 先頭の連続する改行をカウント
704: while (src[start] == '\n') {
705: start++;
706: len--;
707: }
708: std::string stripped = src.substr(start, len);
709:
710: // 面倒なので行頭に関わらず全タブを置換
711: const char *c_stripped = stripped.c_str();
712: std::string dst;
713: for (const char *s = c_stripped; *s; s++) {
714: if (*s == '\t') {
715: dst.append(" ");
716: } else {
717: dst.append(1, *s);
718: }
719: }
720: return dst;
721: }
722:
723: /*static*/ const HelpMessages
1.1.1.6 root 724: Debugger::HelpListMain = {
1.1.1.5 root 725: { "b*", "Set/show Breakpoints (Type \"hb\" for details)" },
726: { "brhist", "Show branch history" },
727: { "c", "Continue" },
728: { "d/dt/D", "Disassemble" },
729: { "disp", "Set register group to show" },
730: { "exhist", "Show exception history" },
731: { "help", "Show this message" },
732: { "L", "Set log level" },
733: { "m/mt/M", "Memory dump" },
734: { "n/nt", "Step until next instruction (Skip subroutines)" },
735: { "quit", "Quit" },
736: { "r*", "Show registers (Type \"hr\" for details)" },
737: { "reset", "Reset the VM" },
738: { "s/st", "Step an instruction (Step in)" },
739: { "so", "Step out" },
740: { "show", "Show monitor" },
741: };
742:
743: /*static*/ const HelpMessages
1.1.1.6 root 744: Debugger::HelpListBreakpoints = {
1.1.1.5 root 745: { "b", "Show all breakpoints" },
746: { "b arg..","Set/Delete breakpoint" },
747: { "bm", "Set memory breakpoint" },
748: { "bi", "Set instruction breakpoint" },
749: { "bv", "Set exception breakpoint" },
750: { "bx", "Delete all breakpoints" },
751: };
752:
753: // 各コマンドの詳細。"コマンド名" => "説明文" の形式。
754: // 説明文は C+11 の生文字リテラル機能を使って R"**( )**" で囲む。
755: // 1つのエントリは
756: // { "cmdname", R"**(
757: // <tab>cmdname [argument...]
758: //
759: // <tab>Description...
760: // <tab>)**" },
761: // のようになる。説明文本文はインデント1つ(TAB 幅 4) で字下げした状態で
762: // 80桁以内に収めること。出力の際にタブを空白4つに置き換えることでソース上
763: // での見た目と実際の出力とを揃えてある。
764: // またソースコード上の見栄えの問題として、文字列の開始記号、終了記号を本文
765: // とは別の行に書いているため、オブジェクトとしての文字列には先頭に改行、
766: // 末尾にタブが余計に入っているが、これは表示の際にコードで取り除く。
767: //
768: // 説明文の書き方は、まずコマンド書式を1行ずつ書く。
769: // コマンド書式と本文との間は1行あける。
770: /*static*/ const HelpMessages
771: Debugger::HelpDetails = {
772: //-----
773: { "b", R"**(
774: Command: b
775: Command: b <address> [<skipcount>]
776: Command: b #n
777:
778: The first form (with no arguments) shows all breakpoints.
779: The second form sets a new breakpoint on <address>.
780: XXX skipcount
781: If <skipcount> is -1, it will never match. It's useful to count the
782: number of times you have passed this address.
783: The third form deletes a breakpoint specified by number.
784: )**" },
785:
786: //-----
787: { "bm", R"**(
788: Command: bm <address> [<skipcount>]
789:
790: Sets a memory breakpoint on <address>.
791: XXX skipcount
792: If <skipcount> is -1, it will never match. It's useful to count the
793: number of times you have passed this address.
794: )**" },
795:
796: //-----
797: { "bi", R"**(
798: Command: bi <inst>[:<mask>] [<skipcount>]
799:
800: Sets an instruction breakpoint. <inst> must be 16 bits or 32 bits on
801: m68k and must be 32 bits on m88k. <mask> can specify the mask. Its
802: length must be the same as <inst>. If the <mask> is ommited, all bits
803: of <inst> is used to compare.
804: For example,
805: "bi 4e75" on m68k will stop at 'rts' instruction.
806: "bi 70004e4f:fff0ffff" on m68k will stop at any of 'IOCS #0'..'IOCS #15'.
807:
808: XXX skipcount
809: If <skipcount> is -1, it will never match. It's useful to count the
810: number of times you have passed this address.
811: )**" },
812:
813: //-----
814: { "bv", R"**(
815: Command: bv <vector>[-<vector2>] [<skipcount>]
816:
817: Sets an exception breakpoint. <vector> must be specified in hex.
818: <vector> ranges from 0 to ff (255 in decimal) on m68k, and 0 to 1ff (511
819: in decimal) on m88k. If <vector2> is specified, it matches the range
820: from <vector> to <vector2> (including themselves).
821: For example, "bv 1-1ff" on m88k means all exceptions but reset.
822: XXX skipcount
823: If <skipcount> is -1, it will never match. It's useful to count the
824: number of times you have passed this address.
825: )**" },
826:
827: //-----
828: { "bx", R"**(
829: Command: bx
830:
831: Deletes all breakpoints.
832: )**" },
833:
834: //-----
835: { "brhist", R"**(
836: Command: brhist [<maxlines>]
837:
838: Show branch history. <maxlines> specifies the maximum number of lines
839: to display. If <maxlines> is negative value, sort in reverse order.
840: )**" },
841:
842: //-----
843: { "c", R"**(
844: Command: c [<address>]
845:
846: Continue (until <address> if specified).
847: )**" },
848:
849: //-----
850: { "d", R"**(
851: Command: D <address>] [<lines>]
1.1.1.6 root 852: Command: d [[<space>:]<address>] [<lines>]
853: Command: dt [[<space>:]<address>] [<lines>]
1.1.1.5 root 854:
855: Shows disassemble.
856: The first form ("D") interprets <address> as a physical address.
857: The second and third form ("d", "dt") interprets <address> as a logical
1.1.1.7 ! root 858: address if the address translation is enabled. Normally, <address> is
1.1.1.6 root 859: interpreted in the current privilege and current address space. You can
860: change it by <space> modifier.
861: On m68k, <space> can be specified either by function code number directly
862: ('1', '2', '5', and '6) or by one or more following modifiers:
863: 's' .. Supervisor mode 'u' .. User mode
864: 'd' .. Data space 'p' or 'i' .. Program space
865: On m88k, <space> can be specified by using combination of privilege
866: modifier ('s' or 'u', same as above) and following CMMU identifiers:
867: 'd' .. Data CMMU 'i' or 'p' .. Instruction CMMU
868: Or CMMU can also be identified by CMMU number (like '6' or '7').
869:
1.1.1.5 root 870: "d" only looks up in ATC (on m68k) or in BATC/PATC (on m88k).
871: "dt" will simulate to search the page table in addition to that.
872: )**" },
873: { "dt", "=d" },
874: { "D", "=d" },
875:
876: //-----
877: { "disp", R"**(
878: Command: disp <register...>
879:
880: Sets the registers list to be shown in the trace.
881: <registers...> is comma-separated list and each of which is a command
882: name that shows registers. See "hr".
883: The default is "r" and thus it only shows general registers in the trace.
884: For example, if you set "disp r,rf", it will show general registers and
885: FPU registers in every trace.
886: )**" },
887:
888: //-----
889: { "exhist", R"**(
890: Command: exhist [<maxlines>]
891:
892: Show exception history. <maxlines> specifies the maximum number of lines
893: to display. If <maxlines> is negative value, sort in reverse order.
894: )**" },
895:
896: //-----
897: { "help", R"**(
898: Command: help [<command>]
899: Command: hb
900: Command: hr
901:
902: The "help" command without arguments shows the list of commands.
903: The "help" command with an argument shows <command>'s detailed help.
904: "h" is a synonym of "help".
905: The "hb" command shows the list of breakpoint-related commands.
906: The "hr" command shows the list of register-related commands.
907: )**" },
908: { "h", "=help" },
909: { "hb", "=help" },
910: { "hr", "=help" },
911:
912: //-----
913: { "L", R"**(
914: Command: L <name> <level>
915:
916: Set loglevel. XXX To be written...
917: )**" },
918:
919: //-----
920: { "m", R"**(
921: Command: M <address>] [<lines>]
1.1.1.6 root 922: Command: m [[<space>:]<address>] [<lines>]
923: Command: mt [[<space>:]<address>] [<lines>]
1.1.1.5 root 924:
925: Shows memory dump.
926: The first form ("M") interprets <address> as a physical address.
927: The second and third form ("m", "mt") interprets <address> as a logical
928: address if the address translation is enabled. Normally, <address> is
1.1.1.6 root 929: interpreted in the current privilege and current address space. You can
930: change it by <space> modifier.
931: On m68k, <space> can be specified either by function code number directly
932: ('1', '2', '5', and '6) or by one or more following modifiers:
933: 's' .. Supervisor mode 'u' .. User mode
934: 'd' .. Data space 'p' or 'i' .. Program space
935: On m88k, <space> can be specified by using combination of privilege
936: modifier ('s' or 'u', same as above) and following CMMU identifiers:
937: 'd' .. Data CMMU 'i' or 'p' .. Instruction CMMU
938: Or CMMU can also be identified by CMMU number (like '6' or '7').
939:
1.1.1.5 root 940: "m" only looks up in ATC (on m68k) or in BATC/PATC (on m88k).
941: "mt" will simulate to search the page table in addition to that.
942: )**" },
943: { "mt", "=m" },
944: { "M", "=m" },
945:
946: //-----
947: { "n", R"**(
948: Command: n [<count>]
949: Command: nt [<count>]
950:
951: Step one (or <count>) instructions. Unlike "s" command, "n" skips
952: subroutine.
953: If "t" is suffixed, it shows a trace for each instruction (including
954: while skipping).
955: )**" },
956: { "nt", "=n" },
957:
958: //-----
959: { "quit", R"**(
960: Command: quit (or q)
961:
962: Quit the debugger console. This continues the execution, as same as "c".
963: )**" },
964: { "q", "=quit" },
965:
966: //-----
967: { "reset", R"**(
968: Command: reset
969:
970: Reset the VM. Currently this does the hardware reset.
971: )**" },
972:
973: //-----
974: { "s", R"**(
975: Command: s [<count>]
976: Command: st [<count>]
977:
978: Step one (or <count>) instructions. Unlike "n" command, "s" steps in
979: subroutine.
980: If "t" is suffixed, it shows a trace for each instruction.
981:
982: "t" command is a synonym of "st" for backward compatibility.
983: )**" },
984: { "st", "=s" },
985:
986: //-----
987: { "so", R"**(
988: Command: so
989: Command: sot
990:
991: Step out this sub routine.
992: If "t" is suffixed, it shows a trace for each instruction.
993: )**" },
994:
995: //-----
996: { "show", R"**(
997: Command: show <monitor>
998:
999: Shows the specified monitor.
1000: )**" },
1001:
1002: //-----
1003: { "t", "=s" },
1004: };
1005:
1.1.1.3 root 1006: // cmdbuf を args... に分解する。
1.1 root 1007: void
1.1.1.3 root 1008: Debugger::ParseCmdbuf()
1.1 root 1009: {
1.1.1.3 root 1010: int pos;
1011: int start;
1012:
1013: pos = 0;
1014: args.clear();
1.1 root 1015:
1016: // 引数を空白で分解
1.1.1.3 root 1017: for (;;) {
1018: // 先頭の空白はスキップ
1019: for (; cmdbuf[pos] != '\0'; pos++) {
1020: if (!isspace((unsigned int)cmdbuf[pos]))
1021: break;
1022: }
1023: if (cmdbuf[pos] == '\0')
1.1 root 1024: break;
1025:
1.1.1.3 root 1026: // 終わりを探す
1027: start = pos;
1028: for (; cmdbuf[pos] != '\0'; pos++) {
1029: if (isspace((unsigned int)cmdbuf[pos]))
1.1 root 1030: break;
1031: }
1.1.1.3 root 1032: args.push_back(cmdbuf.substr(start, pos - start));
1.1.1.2 root 1033: }
1034: // デバッグ表示
1035: if (0) {
1.1.1.3 root 1036: for (int i = 0 ; i < args.size(); i++) {
1037: printf("args[%d]=|%s|\n", i, args[i].c_str());
1.1.1.2 root 1038: }
1039: }
1.1 root 1040: }
1041:
1042: //
1043: // デバッガコマンド
1044: //
1045:
1046: // ブレークポイント
1.1.1.4 root 1047: // b ... 一覧表示
1048: // b #n ... #n を削除
1049: // b <addr> [<skip>] ... 設定
1.1 root 1050: void
1051: Debugger::cmd_b()
1052: {
1053: // 引数なしなら一覧表示
1.1.1.3 root 1054: if (args.size() < 2) {
1.1 root 1055: return cmd_b_list();
1056: }
1057:
1058: // 引数取得
1.1.1.3 root 1059: if (args[1][0] == '#') {
1.1 root 1060: // #<n> 形式なら、指定番号のブレークポイントを削除
1.1.1.3 root 1061: int i = atoi(&args[1][1]);
1062: if (i < 0 || i >= bpoint.size()) {
1.1.1.4 root 1063: cons->Print("invaild breakpoint number: #%d\n", i);
1.1 root 1064: return;
1065: }
1.1.1.3 root 1066: auto& bp = bpoint[i];
1.1.1.4 root 1067: if (bp.type == BreakpointType::Unused) {
1068: cons->Print("invalid breakpoint number: #%d\n", i);
1069: return;
1.1 root 1070: }
1.1.1.4 root 1071: cons->Print("breakpoint #%d (%08x) removed\n",
1072: i, bp.addr);
1073: bp.type = BreakpointType::Unused;
1074: // 今登録されている命令ブレークの必要命令長を再計算
1075: RecalcInstMask();
1.1 root 1076: return;
1077: }
1078:
1.1.1.4 root 1079: return cmd_b_set(BreakpointType::Address);
1080: }
1081:
1082: // メモリブレークポイントの設定
1083: // bm <addr> [<skip>]
1084: void
1085: Debugger::cmd_bm()
1086: {
1087: // XXX m68k では未サポート
1.1.1.6 root 1088: if (md->arch == Arch::M680x0) {
1.1.1.4 root 1089: cons->Print("bm not supported yet on m68k\n");
1090: return;
1091: }
1092: cmd_b_set(BreakpointType::Memory);
1093: }
1094:
1095: // type が違うだけの各種ブレークポイント設定の共通部分。
1096: void
1097: Debugger::cmd_b_set(BreakpointType type)
1098: {
1099: breakpoint_t bp;
1100:
1101: if (args.size() < 2) {
1102: cons->Print("usage: %s <addr> [<skipcount>]\n", args[0].c_str());
1103: return;
1104: }
1105:
1106: // アドレス
1107: if (!ParseAddr(args[1].c_str(), &bp.addr)) {
1108: return;
1109: }
1110: // あればスキップカウント
1111: if (args.size() > 2) {
1112: bp.skip = atoi(args[2].c_str());
1113: }
1114:
1115: // 空いてるところにセット
1116: // (よく似たエントリがあっても干渉しない)
1117: bp.type = type;
1118: int bi = AddBreakpoint(bp);
1119: if (bi == -1) {
1120: cons->Print("no free breakpoints\n");
1.1 root 1121: } else {
1.1.1.4 root 1122: cons->Print("breakpoint #%d added\n", bi);
1.1.1.3 root 1123: }
1.1.1.4 root 1124: }
1125:
1126: // 命令ブレークポイントの設定
1127: // bi <insn>[:<mask>] [<skip>]
1128: void
1129: Debugger::cmd_bi()
1130: {
1131: breakpoint_t bp;
1132: std::string inststr;
1133: std::string maskstr;
1134: int instlen;
1135: int masklen;
1136:
1137: if (args.size() < 2) {
1138: cons->Print("usage: bi <inst>[:<mask>] [<skipcount>]\n");
1.1.1.3 root 1139: return;
1.1 root 1140: }
1141:
1.1.1.4 root 1142: // 引数をまず分離
1143: auto pos = args[1].find(':');
1144: if (pos == std::string::npos) {
1145: // マスク指定なし
1146: inststr = args[1];
1147: instlen = inststr.size();
1148: masklen = -1;
1149: } else {
1150: // マスク指定あり
1151: inststr = args[1].substr(0, pos);
1152: instlen = inststr.size();
1153: maskstr = args[1].substr(pos + 1);
1154: masklen = maskstr.size();
1155: }
1156:
1157: // 命令部チェック
1158: if (ParseVerbHex(inststr.c_str(), &bp.inst) == false) {
1159: cons->Print("%s: invalid instruction value\n", args[1].c_str());
1160: return;
1161: }
1.1.1.5 root 1162: if (instlen % (md->inst_bytes * 2) != 0) {
1.1.1.4 root 1163: cons->Print("%s: invalid instruction length\n", args[1].c_str());
1164: return;
1165: }
1166:
1167: // マスク部チェック
1168: bp.mask = 0xffffffff;
1169: if (masklen != -1) {
1170: if (ParseVerbHex(maskstr.c_str(), &bp.mask) == false) {
1171: cons->Print("%s: invalid mask value\n", args[1].c_str());
1172: return;
1173: }
1174: if (masklen != instlen) {
1175: cons->Print("%s: inst:mask must be the same length\n",
1176: args[1].c_str());
1.1 root 1177: return;
1178: }
1179: }
1.1.1.4 root 1180: // 8バイト未満なら左詰め。
1.1.1.5 root 1181: if (md->inst_bytes < 4 && instlen < 8) {
1.1.1.4 root 1182: bp.inst <<= 32 - instlen * 4;
1183: bp.mask <<= 32 - instlen * 4;
1184: }
1185:
1186: // あればスキップカウント
1187: bp.skip = 0;
1188: if (args.size() > 2) {
1189: bp.skip = atoi(args[2].c_str());
1190: }
1191:
1192: // 空いてるところにセット
1193: bp.type = BreakpointType::Instruction;
1194: int bi = AddBreakpoint(bp);
1195: if (bi == -1) {
1196: cons->Print("no free breakpoints\n");
1197: } else {
1198: cons->Print("breakpoint #%d added\n", bi);
1199: }
1200:
1201: // 今登録されている命令ブレークの必要命令長を再計算
1202: RecalcInstMask();
1203: }
1204:
1205: // 例外ブレークポイントの設定
1206: // bv <vector_number>[-<end_number>] [<skip>]
1207: void
1208: Debugger::cmd_bv()
1209: {
1210: breakpoint_t bp;
1211:
1212: if (args.size() < 2) {
1213: cons->Print("usage: be <vec#>[-<end#>] [<skipcount>]\n");
1214: return;
1215: }
1216:
1217: auto pos = args[1].find('-');
1218: if (pos == std::string::npos) {
1219: // ベクタ番号が1つなら vec1, vec2 を同値にしておく。
1220: if (ParseVerbHex(args[1].c_str(), (uint32 *)&bp.vec1) == false) {
1221: cons->Print("%s: invalid vector number\n", args[1].c_str());
1222: return;
1223: }
1224: bp.vec2 = bp.vec1;
1225: } else {
1226: // ベクタ番号(範囲指定 [vec1, vec2])
1227: std::string str1 = args[1].substr(0, pos);
1228: std::string str2 = args[1].substr(pos + 1);
1229:
1230: if (ParseVerbHex(str1.c_str(), (uint32 *)&bp.vec1) == false) {
1231: cons->Print("%s: invalid first vector number\n", args[1].c_str());
1232: return;
1233: }
1234: if (ParseVerbHex(str2.c_str(), (uint32 *)&bp.vec2) == false) {
1235: cons->Print("%s: invalid last vector number\n", args[1].c_str());
1236: return;
1237: }
1238: }
1239:
1240: // 範囲チェック
1241: if (bp.vec1 < 0 || bp.vec1 >= md->vector_max) {
1242: cons->Print("$%x: invalid vector number\n", bp.vec1);
1243: return;
1244: }
1245: if (bp.vec2 < 0 || bp.vec2 >= md->vector_max) {
1246: cons->Print("$%x: invalid last vector number\n", bp.vec2);
1247: return;
1248: }
1249:
1250: // 大小が逆なら入れ替える?
1251: if (bp.vec1 > bp.vec2) {
1252: int tmp;
1253: tmp = bp.vec1;
1254: bp.vec1 = bp.vec2;
1255: bp.vec2 = tmp;
1256: }
1257:
1258: // あればスキップカウント
1259: bp.skip = 0;
1260: if (args.size() > 2) {
1261: bp.skip = atoi(args[2].c_str());
1262: }
1.1 root 1263:
1264: // 空いてるところにセット
1.1.1.4 root 1265: bp.type = BreakpointType::Exception;
1266: int bi = AddBreakpoint(bp);
1.1 root 1267: if (bi == -1) {
1.1.1.4 root 1268: cons->Print("no free breakpoints\n");
1.1 root 1269: } else {
1.1.1.4 root 1270: cons->Print("breakpoint #%d added\n", bi);
1.1 root 1271: }
1.1.1.5 root 1272:
1273: // すでに来ている例外をクリア。
1274: // ブレークポイント設定の有無に関わらず例外が起きたら CPU 側から常に
1275: // 通知されている。これをクリアするのは CheckAllBreakpoints() で、これは
1276: // 命令間(命令前)に呼ばれるやつ、なのでこうなる。
1277: // 1. 例外が起きると bv_vector がセットされる
1278: // 2. 例外ブレークポイントを設定していないとこれがクリアされない
1279: // 3. bv コマンドで例外ブレークを新たに設定すると、次の命令境界で
1280: // 1.のベクタが反応してしまう。
1281: // 命令ごととかにクリアしてもいいかも知れないが、ここでブレークポイントを
1282: // 設定したのだから、それ以前の事象には反応すべきでない、という意味では
1283: // ここでもいいか?
1284: bv_vector = -1;
1.1 root 1285: }
1286:
1287: // ブレークポイント一覧表示
1288: void
1289: Debugger::cmd_b_list()
1290: {
1.1.1.4 root 1291: ShowMonitor(*gBreakpointMonitor);
1292: }
1293:
1294: // ブレークポイント一覧 (モニタ)
1295: void
1296: Debugger::MonitorBreakpoint(TextScreen& monitor)
1297: {
1298: // 0 1 2 3 4 5 6
1299: // 0123456789012345678901234567890123456789012345678901234567890
1300: // No Type Parameter Matched Skip
1301: // #0 addr $01234567 123456789 123456789/123456789
1302: // #1 inst 00000000/00000000
1303: // #2 excp $00-$00
1304:
1305: monitor.Clear();
1306: monitor.Print(0, 0, "No Type Parameter");
1307: monitor.Print(26, 0, "Matched");
1308: monitor.Print(37, 0, "Skip");
1.1 root 1309:
1.1.1.3 root 1310: for (int i = 0; i < bpoint.size(); i++) {
1.1.1.4 root 1311: const auto& bp = bpoint[i];
1312: int y = i + 1;
1313:
1314: monitor.Print(0, y, "#%d", i);
1315:
1316: // 種別ごとの表示
1317: switch (bp.type) {
1318: case BreakpointType::Unused:
1319: continue;
1320:
1321: case BreakpointType::Address:
1322: monitor.Print(3, y, "addr $%08x", bp.addr);
1323: break;
1324: case BreakpointType::Memory:
1325: monitor.Print(3, y, "mem $%08x", bp.addr);
1326: break;
1327:
1328: case BreakpointType::Exception:
1329: monitor.Print(3, y, "excp $%02x", bp.vec1);
1330: if (bp.vec2 != bp.vec1) {
1331: monitor.Print(11, y, "-$%02x", bp.vec2);
1332: }
1333: break;
1334:
1335: case BreakpointType::Instruction:
1336: monitor.Print(3, y, "inst");
1.1.1.5 root 1337: if (md->inst_bytes == 4) {
1.1.1.4 root 1338: if (bp.mask == 0xffffffff) {
1339: monitor.Print(8, y, "%08x", bp.inst);
1340: } else {
1341: monitor.Print(8, y, "%08x:%08x", bp.inst, bp.mask);
1342: }
1343: } else {
1344: if (bp.mask == 0xffffffff) {
1345: monitor.Print(8, y, "%08x", bp.inst);
1346: } else if (((bp.inst | bp.mask) & 0x0000ffff) != 0) {
1347: monitor.Print(8, y, "%08x:%08x", bp.inst, bp.mask);
1348: } else if (bp.mask == 0xffff0000) {
1349: monitor.Print(8, y, "%04x", bp.inst >> 16);
1350: } else {
1351: monitor.Print(8, y, "%04x:%04x",
1352: bp.inst >> 16, bp.mask >> 16);
1353: }
1354: }
1355: break;
1356:
1357: default:
1358: monitor.Print(3, y, "type=%d", (int)bp.type);
1359: continue;
1360: }
1361:
1362: // マッチ回数
1363: monitor.Print(26, y, "%d", bp.matched);
1364:
1365: // スキップ
1366: if (bp.skip < 0) {
1367: monitor.Print(37, y, "forever");
1368: } else if (bp.skip > 0) {
1369: monitor.Print(37, y, "%d / %d", (bp.skip - bp.skipremain), bp.skip);
1.1 root 1370: }
1371: }
1372: }
1373:
1374: // ブレークポイント全削除
1375: void
1376: Debugger::cmd_bx()
1377: {
1.1.1.3 root 1378: for (auto& bp : bpoint) {
1.1.1.4 root 1379: bp.type = BreakpointType::Unused;
1.1 root 1380: }
1381: cons->Print(" All breakpoints disabled\n");
1.1.1.4 root 1382:
1383: // 今登録されている命令ブレークの必要命令長を再計算
1384: RecalcInstMask();
1.1 root 1385: }
1386:
1387: // ブレークポイントを設定。
1.1.1.4 root 1388: // new_bp のうち matched, skipremain はこちらで初期化する。
1389: // それ以外を埋めてから呼ぶこと。
1390: // 設定できればその番号、できなければ -1 を返す。
1.1 root 1391: int
1.1.1.4 root 1392: Debugger::AddBreakpoint(const breakpoint_t& new_bp)
1.1 root 1393: {
1.1.1.3 root 1394: for (int i = 0; i < bpoint.size(); i++) {
1395: auto& bp = bpoint[i];
1.1.1.4 root 1396: if (bp.type == BreakpointType::Unused) {
1397: bp = new_bp;
1398: bp.matched = 0;
1399: if (bp.skip > 0) {
1400: bp.skipremain = bp.skip;
1401: } else {
1402: bp.skipremain = 0;
1403: }
1404:
1.1 root 1405: return i;
1406: }
1407: }
1408: return -1;
1409: }
1410:
1.1.1.4 root 1411: // すべての命令ブレークのマスクのうち最長のものを再計算する。
1412: // 命令ブレークポイントの追加/削除のたびに呼び出すこと。
1413: void
1414: Debugger::RecalcInstMask()
1415: {
1416: uint32 mask;
1417: bi_need_bytes = 0;
1418:
1419: // 登録されている命令ブレークの最長マスクを求める
1420: mask = 0;
1421: for (const auto& bp : bpoint) {
1422: if (bp.type == BreakpointType::Instruction) {
1423: mask |= bp.mask;
1424: }
1425: }
1426:
1427: // mask の Number of Trailing Zero を求める。
1428: // (x & -x) で x の最も下の立ってるビットだけを立てる、
1429: // (x & -x) -1 でそれより下の全ビットを立てる、
1430: // それを popcount で数えるので、$fffffff0 なら ntz = 4 になる。
1431: int ntz = __builtin_popcount((mask & -(int32)mask) - 1);
1432: // 上位側から数えたマスクに必要なビット数
1433: int mlen = 32 - ntz;
1434: // 命令語単位に切り上げる
1.1.1.5 root 1435: mlen = roundup(mlen, md->inst_bytes * 8);
1.1.1.4 root 1436: // バイト数に変換
1437: mlen /= 8;
1438:
1439: if (mlen > bi_need_bytes) {
1440: bi_need_bytes = mlen;
1441: }
1442: }
1443:
1444: // ブランチ履歴、例外履歴表示
1445: // brhist [<maxlines>]
1446: // exhist [<maxlines>]
1447: // <maxlines> は表示する最大エントリ数。
1448: // コンソールではデフォルトで下を新しいの順とする。<maxlines> を負数にすると
1449: // (行数は絶対値して) 並び順を逆にしてモニタウィンドウと同じ上を新しいの順に
1450: // する。
1.1 root 1451: void
1452: Debugger::cmd_brhist()
1453: {
1.1.1.4 root 1454: cmd_hist_common(md->GetBrHist(), 0);
1455: }
1456: void
1457: Debugger::cmd_exhist()
1458: {
1459: cmd_hist_common(md->GetExHist(), BranchHistory::ExHist);
1460: }
1.1.1.3 root 1461:
1.1.1.4 root 1462: // ブランチ履歴、例外履歴表示の共通部分。
1463: void
1464: Debugger::cmd_hist_common(BranchHistory& hist, uint64 flag)
1465: {
1466: // 表示最大行数(と向き)
1467: // 向きは bottom_to_top = true が新しいほうを下とする方向。
1468: int maxlines = 20;
1469: bool bottom_to_top = true;
1.1.1.3 root 1470: if (args.size() > 1) {
1.1.1.4 root 1471: maxlines = atoi(args[1].c_str());
1472: if (maxlines < 0) {
1473: bottom_to_top = false;
1474: maxlines = -maxlines;
1.1.1.3 root 1475: }
1.1.1.4 root 1476:
1477: if (maxlines < 1)
1478: maxlines = 1;
1479: if (maxlines > 256)
1480: maxlines = 256;
1481: }
1482:
1483: // コンソールでは空エントリの行は表示したくないので、
1484: // 先にエントリ数を調べる。
1485: int used = hist.GetUsed();
1486:
1487: // 表示行数 + 1行はヘッダ分
1488: int lines = std::min(used, maxlines) + 1;
1489:
1490: // MonitorUpdate() は TextScreen 高さに合わせて出力してくれる。
1491: auto size = hist.GetMonitorSize();
1492: TextScreen tscr;
1493: tscr.Init(size.width, lines);
1494:
1495: // コンソールでは下が新しいの順のほうがいい
1496: if (bottom_to_top) {
1497: flag |= BranchHistory::BottomToTop;
1.1.1.3 root 1498: }
1.1.1.4 root 1499: tscr.userdata = flag;
1500:
1501: // 表示
1502: hist.MonitorUpdate(tscr);
1503: ShowTextScreen(tscr);
1.1 root 1504: }
1505:
1506: // 実行再開(continue): c [<addr>]
1507: // <addr> 指定があれば <addr> まで実行。
1508: void
1509: Debugger::cmd_c()
1510: {
1.1.1.3 root 1511: if (args.size() > 1) {
1.1 root 1512: // 引数があれば
1.1.1.3 root 1513: if (!ParseAddr(args[1].c_str(), &bc_addr)) {
1.1 root 1514: return;
1515: }
1516: // 偶数番地に丸める
1517: bc_addr &= 0xfffffffe;
1518:
1519: bc_enable = true;
1520: }
1521: }
1522:
1.1.1.5 root 1523: // 引数などからアドレスを取得するごった煮共通ルーチン。
1524: //
1.1.1.6 root 1525: // 引数がなければ sa->addr には書き込まずに true で帰る (ので、呼び出し側が
1526: // 事前に適切なデフォルト値をセットしておくと、それがそのまま使われる)。
1.1.1.3 root 1527: // 引数が1つ(以上)あれば args[1] をアドレスとしてパースする。
1.1.1.6 root 1528: // アドレスには空間修飾子を前置可能、省略の場合はいずれも現在値を使う。
1529: // エラーならメッセージを表示して false を返す。
1.1.1.5 root 1530: // sa は in/out パラメータ。
1.1 root 1531: bool
1.1.1.6 root 1532: Debugger::GetAddr(saddr_t *sa, bool default_data)
1.1 root 1533: {
1.1.1.5 root 1534: if (args.size() < 2) {
1.1.1.6 root 1535: // 引数なしなら、呼び出し側が sa にセットした前回値をそのまま使う。
1.1.1.5 root 1536: return true;
1537: }
1538:
1.1.1.6 root 1539: // 引数ありならパースする。アドレスが明示的に指定されたので、
1540: // ここから先の省略箇所は前回値ではなくデフォルト値で補完する。
1541:
1.1.1.5 root 1542: std::string& str = args[1];
1543: uint32 addr;
1544:
1.1.1.6 root 1545: // ':' があればその前はアドレス空間修飾子
1546: int mod_super = -1; // 'u'/'s'
1547: int mod_prog = -1; // 'd'/'p' or 'd'/'i'
1548: int mod_num = -1; // '0'..'7'
1549: auto addrpos = str.find(':');
1550: if (addrpos == std::string::npos) {
1551: // なければ先頭からアドレス
1552: addrpos = 0;
1553: } else {
1554: // ':' が途中にあれば、その前が修飾子。
1555: // ':' が先頭なら修飾子が空文字列という扱いでいいか。
1556: std::string mod = str.substr(0, addrpos);
1557: addrpos++;
1558: for (auto ch : mod) {
1559: if (ch == 'u') {
1560: if (mod_super == 1)
1561: goto error;
1562: mod_super = 0;
1563: } else if (ch == 's') {
1564: if (mod_super == 0)
1565: goto error;
1566: mod_super = 1;
1567: } else if (ch == 'd') {
1568: if (mod_prog == 1)
1569: goto error;
1570: mod_prog = 0;
1571: } else if (ch == 'p' || ch == 'i') {
1572: // m68k では 'P'rogram space、m88k では 'I'nstruction CMMU…
1573: if (mod_prog == 0)
1574: goto error;
1575: mod_prog = 1;
1576: } else if ('0' <= ch && ch <= '7') {
1577: int num = ch - '0';
1578: if (md->arch == Arch::M680x0) {
1579: // m68k では FC 指定は Super/User 指定を含んでおり、
1580: // 値指定は他の修飾子とは同時に指定できない。
1581: if (mod_super >= 0 || mod_prog >= 0 ||
1582: (mod_num >= 0 && mod_num != num)) {
1583: goto error;
1584: }
1585: // 有効な値は 1,2,5,6 のみ
1586: if (num == 0 || num == 3 || num == 4 || num == 7) {
1587: cons->Print("%c: invalid address space\n", ch);
1588: }
1589: mod_num = num;
1590: mod_super = mod_num >> 2; // FC2
1591: mod_prog = (mod_num & 3) - 1; // FC1,FC0
1592: } else if (md->arch == Arch::M88xx0) {
1593: // m88k では CMMU 指定と、Super/User 指定は独立。
1594: if (mod_prog >= 0 ||
1595: (mod_num >= 0 && mod_num != num)) {
1596: goto error;
1597: }
1598: mod_num = num;
1599: mod_prog = (mod_num & 1); // CMMU7 が Inst
1600: }
1601: } else {
1602: cons->Print("%c: unknown address space modifier\n", ch);
1603: return false;
1604: }
1605: continue;
1606:
1607: error:
1608: cons->Print("%s: address space modifier '%c' conflicts\n",
1609: mod.c_str(), ch);
1610: return false;
1611: }
1612:
1613: // 数値指定を機種ごとに読み替える
1614: if (mod_num >= 0) {
1615: if (md->arch == Arch::M680x0) {
1616: // m68k なら値指定で全部確定する
1617: // "1:" -> User/Data
1618: // "2:" -> User/Program
1619: // "5:" -> Super/Data
1620: // "6:" -> Super/Program
1621: mod_super = (mod_num & 4) ? true : false;
1622: mod_prog = (mod_num & 3) - 1;
1623: } else if (md->arch == Arch::M88xx0) {
1624: // m88k では mod_num を CMMU ID とする(?)。
1625: // XXX まだ CPU は1つしかないので雑に処理。
1626: // "6" -> Data (CPU#0)
1627: // "7" -> Program (CPU#0)
1628: if (mod_num < 6) {
1629: cons->Print("%d: CMMU#%d not installed\n",
1630: mod_num, mod_num);
1631: return false;
1632: }
1633: mod_prog = mod_num - 6;
1634: }
1635: }
1.1 root 1636: }
1.1.1.5 root 1637:
1.1.1.6 root 1638: // アドレス空間修飾子のうち省略箇所はデフォルト状態で補完
1639: // - 's'/'u' いずれもなければ現在値。
1640: // - 'd'/'p'/'i' いずれもなければ、d/m コマンドごとに自然なほう。
1641: if (mod_super < 0) {
1642: mod_super = md->IsSuper();
1643: }
1644: if (mod_prog < 0) {
1645: mod_prog = !default_data;
1646: }
1647: sa->SetSuper(mod_super);
1648: sa->SetData(!mod_prog);
1649:
1.1.1.5 root 1650: // アドレス
1651: if (ParseAddr(str.c_str() + addrpos, &addr) == false) {
1652: return false;
1653: }
1.1.1.7 ! root 1654: // 命令境界に丸める
! 1655: sa->SetAddr(addr & ~(md->inst_bytes - 1));
1.1.1.5 root 1656:
1.1 root 1657: return true;
1658: }
1659:
1.1.1.5 root 1660: // 逆アセンブル: d [[SU:]<addr> [<cnt>]] (論理、テーブルサーチなし)
1.1 root 1661: void
1662: Debugger::cmd_d()
1663: {
1.1.1.6 root 1664: cmd_d_common(MemoryMode::Logical, MMULookupMode::False);
1.1 root 1665: }
1666:
1.1.1.5 root 1667: // 逆アセンブル: dt [[SU:]<addr>] [<cnt>]] (論理、テーブルサーチあり)
1.1 root 1668: void
1669: Debugger::cmd_dt()
1670: {
1.1.1.6 root 1671: cmd_d_common(MemoryMode::Logical, MMULookupMode::True);
1.1 root 1672: }
1673:
1674: // 逆アセンブル: D [<addr> [<cnt>]] (物理)
1675: void
1676: Debugger::cmd_D()
1677: {
1.1.1.6 root 1678: cmd_d_common(MemoryMode::Physical, MMULookupMode::None);
1.1 root 1679: }
1680:
1681: // 逆アセンブル共通
1682: void
1.1.1.6 root 1683: Debugger::cmd_d_common(MemoryMode access_mode, MMULookupMode lookup_mode)
1.1 root 1684: {
1.1.1.2 root 1685: saddr_t saddr;
1.1 root 1686: int cnt;
1687:
1688: cnt = 10;
1689:
1690: // 開始アドレス
1.1.1.6 root 1691: saddr = d_last_addr;
1692: if (GetAddr(&saddr, false) == false) {
1.1 root 1693: return;
1694: }
1695: // 2つ目の引数があれば行数
1.1.1.3 root 1696: if (args.size() > 2) {
1697: cnt = strtol(args[2].c_str(), NULL, 10);
1.1 root 1698: }
1699:
1.1.1.6 root 1700: DebuggerMemoryStream mem(md, saddr, access_mode, lookup_mode);
1.1.1.2 root 1701:
1.1 root 1702: for (int i = 0; i < cnt; i++) {
1.1.1.2 root 1703: std::string addrbuf;
1704: std::string mnemonic;
1705: std::string mnembuf;
1706: std::vector<uint8> local_ir;
1707:
1.1.1.6 root 1708: // アドレス
1709: if (mem.FormatAddr(addrbuf) == false) {
1710: // アドレス変換でバスエラーならここで終了
1711: cons->Print("%s\n", addrbuf.c_str());
1712: break;
1.1 root 1713: }
1.1.1.6 root 1714:
1715: // 逆アセンブル
1716: md->Disassemble(mem, mnemonic, local_ir);
1717: // オフライン用に整形
1718: mnembuf = md->FormatDisasm(mnemonic, local_ir);
1719:
1.1.1.2 root 1720: cons->Print("%-18s %s\n", addrbuf.c_str(), mnembuf.c_str());
1721:
1722: // 変換できなければここで終了
1723: if (local_ir.size() == 0)
1724: break;
1.1 root 1725: }
1.1.1.2 root 1726:
1.1.1.6 root 1727: // アドレスと Super/User 状態を次回継続用に保存
1728: d_last_addr = mem.laddr;
1.1 root 1729: }
1730:
1.1.1.5 root 1731: // 表示レジスタ選択
1732: // disp 現在の内容を表示
1733: // disp <regs>... 設定
1734: void
1735: Debugger::cmd_disp()
1736: {
1737: if (args.size() < 2) {
1738: // 表示
1739: bool first = true;
1740: for (const auto& r : disp_regs) {
1741: cons->Print("%s%s", (first ? "" : ","), r.c_str());
1742: first = false;
1743: }
1744: cons->Print("\n");
1745: return;
1746: }
1747:
1748: // 設定 (引数を分解する)
1749: char buf[256];
1750: char *p;
1751: char *last;
1752: strlcpy(buf, args[1].c_str(), sizeof(buf));
1753: disp_regs.clear();
1754: for (p = strtok_r(buf, ",", &last); p; p = strtok_r(NULL, ",", &last)) {
1755: disp_regs.push_back(std::string(p));
1756: }
1757:
1758: // XXX チェック
1759: }
1760:
1.1 root 1761: // ログレベル設定: L <name> <level>
1762: void
1763: Debugger::cmd_L()
1764: {
1765: const char *name;
1766: int level;
1767:
1.1.1.3 root 1768: if (args.size() < 3) {
1.1 root 1769: cons->Print("L <name> <level>\n");
1770: return;
1771: }
1772:
1.1.1.3 root 1773: name = args[1].c_str();
1774: level = atoi(args[2].c_str());
1.1 root 1775:
1776: // 短縮形とかエイリアスとか
1777: // lib/mainapp.cpp に同じものがあるのでどうにかしたほうがいい
1778: if (strcmp(name, "sch") == 0)
1779: name = "scheduler";
1780: if (strcmp(name, "scc") == 0)
1781: name = "sio";
1782:
1.1.1.3 root 1783: // 複数のオブジェクトが同じ logname を持ってもよい (CMMUとか)。
1.1 root 1784: // all はどうするか
1785: std::string sname = name;
1786: bool found = false;
1787: for (auto& obj : gObjects) {
1788: if (obj->logname == sname) {
1789: obj->loglevel = level;
1790: cons->Print("%s=%d\n", name, obj->loglevel);
1791: found = true;
1792: }
1793: }
1794: // 見付からない場合はエラー
1795: if (!found) {
1796: cons->Print("%s: invalid logname\n", name);
1797: }
1798: }
1799:
1800: // メモリダンプ: m [<addr> [<cnt>]] (論理、テーブルサーチなし)
1801: void
1802: Debugger::cmd_m()
1803: {
1.1.1.6 root 1804: cmd_m_common(MemoryMode::Logical, MMULookupMode::False);
1.1 root 1805: }
1806:
1807: // メモリダンプ: mt [<addr> [<cnt>]] (論理、テーブルサーチあり)
1808: void
1809: Debugger::cmd_mt()
1810: {
1.1.1.6 root 1811: cmd_m_common(MemoryMode::Logical, MMULookupMode::True);
1.1 root 1812: }
1813:
1814: // メモリダンプ: M [<addr> [<cnt>]] (物理)
1815: void
1816: Debugger::cmd_M()
1817: {
1.1.1.6 root 1818: cmd_m_common(MemoryMode::Physical, MMULookupMode::None);
1.1 root 1819: }
1820:
1821: // メモリダンプ共通
1822: void
1.1.1.6 root 1823: Debugger::cmd_m_common(MemoryMode access_mode, MMULookupMode lookup_mode)
1.1 root 1824: {
1.1.1.2 root 1825: saddr_t saddr;
1.1 root 1826: int cnt;
1827:
1828: cnt = 10;
1829:
1830: // 開始アドレス
1.1.1.6 root 1831: saddr = m_last_addr;
1832: if (GetAddr(&saddr, true) == false) {
1.1 root 1833: return;
1834: }
1835: // 2つ目の引数があれば行数
1.1.1.3 root 1836: if (args.size() > 2) {
1837: cnt = strtol(args[2].c_str(), NULL, 10);
1.1 root 1838: }
1839:
1.1.1.6 root 1840: DebuggerMemoryStream mem(md, saddr, access_mode, lookup_mode);
1.1.1.2 root 1841:
1.1 root 1842: for (int i = 0; i < cnt; i++) {
1.1.1.2 root 1843: std::string addrbuf;
1844: std::string dumpbuf;
1845: std::string charbuf;
1846:
1.1.1.6 root 1847: // アドレス
1848: if (mem.FormatAddr(addrbuf) == false) {
1849: // アドレス変換でバスエラーならここで終了
1.1.1.2 root 1850: cons->Print("%s\n", addrbuf.c_str());
1851: break;
1852: }
1853:
1854: for (int j = 0; j < 8; j++) {
1855: for (int k = 0; k < 2; k++) {
1.1.1.6 root 1856: uint64 b = mem.Fetch8();
1.1.1.2 root 1857:
1858: if ((int64)b < 0) {
1859: dumpbuf += "--";
1860: charbuf += " ";
1861: } else {
1862: uint32 c = (uint32)b;
1863: dumpbuf += string_format("%02x", c);
1864: charbuf += string_format("%c",
1865: (0x20 <= c && c <= 0x7e) ? c : '.');
1866: }
1867: }
1868: dumpbuf += " ";
1869: }
1.1.1.6 root 1870:
1.1.1.2 root 1871: cons->Print("%-18s %-40s %s\n",
1872: addrbuf.c_str(), dumpbuf.c_str(), charbuf.c_str());
1.1 root 1873: }
1.1.1.6 root 1874:
1875: // アドレスと Super/User 状態を次回継続用に保存
1876: m_last_addr = mem.laddr;
1.1 root 1877: }
1878:
1879: // プロンプトに来た最初に表示するいつものやつを再表示する
1880: void
1881: Debugger::cmd_minus()
1882: {
1.1.1.5 root 1883: // 指定のレジスタ群を表示
1884: // disp_regs はレジスタ群のリスト { "r", "rf" } のような感じ。
1885: // 一方 ShowRegisters() の第2引数は1つのコマンドラインを空白で区切った
1886: // 文字列リスト { arg[0], arg[1], .. }。型が同じで意味が違うので注意。
1887: for (const auto& reg : disp_regs) {
1888: std::vector<std::string> tmpargs;
1889: tmpargs.push_back(reg);
1890: md->ShowRegister(cons, tmpargs);
1891: }
1.1 root 1892:
1.1.1.2 root 1893: // 現在の PC の位置を逆アセンブル。ここで ir を更新。
1894: std::string addrbuf;
1895: std::string mnemonic;
1896: std::string mnembuf;
1897: ir.clear();
1.1.1.6 root 1898:
1899: // 論理アクセス時、ATC ミスが分かったほうが便利だと思うので lookup なし
1900: saddr_t laddr(pc, md->IsSuper());
1901: DebuggerMemoryStream mem(md, laddr);
1902: // アドレス
1903: if (mem.FormatAddr(addrbuf) == false) {
1904: cons->Print("%s\n", addrbuf.c_str());
1905: return;
1.1.1.2 root 1906: }
1.1.1.6 root 1907: // 逆アセンブル
1908: md->Disassemble(mem, mnemonic, ir);
1909: // ライブ表示用に整形
1910: mnembuf = md->FormatDisasmLive(mnemonic, ir);
1911:
1.1.1.2 root 1912: cons->Print("%-18s %s\n", addrbuf.c_str(), mnembuf.c_str());
1.1 root 1913: }
1914:
1.1.1.5 root 1915: // サブルーチンとループを飛ばしながら count 命令ステップ実行
1916: // n [<count>?:1] (途中レジスタを表示しない)
1.1 root 1917: void
1918: Debugger::cmd_n()
1919: {
1.1.1.5 root 1920: cmd_n_common(false);
1921: }
1922:
1923: // サブルーチンとループを飛ばしながら count 命令ステップ実行
1924: // nt [<count>?:1] (1命令ずつレジスタを表示する)
1925: void
1926: Debugger::cmd_nt()
1927: {
1928: cmd_n_common(true);
1929: }
1930:
1931: // n と nt の共通部分
1932: void
1933: Debugger::cmd_n_common(bool trace)
1934: {
1935: t_enable = trace;
1936:
1.1.1.3 root 1937: if (args.size() > 1) {
1.1 root 1938: int count;
1.1.1.3 root 1939: count = strtol(args[1].c_str(), NULL, 10);
1.1 root 1940: if (count < 1) {
1941: cons->Print(" invalid step count: %d\n", count);
1942: return;
1943: }
1944:
1945: n_count = count;
1946: } else {
1947: n_count = 1;
1948: }
1949: n_enable = true;
1950: SetNBreakpoint();
1951: }
1952:
1.1.1.5 root 1953: // n コマンド用のブレークポイントを設定する。
1954: // この命令語によって仕掛けるブレークポイントが変わるので、都度都度
1955: // 呼び出すこと。
1956: void
1.1 root 1957: Debugger::SetNBreakpoint()
1958: {
1.1.1.6 root 1959: saddr_t laddr(md->GetPC(), md->IsSuper());
1960: DebuggerMemoryStream mem(md, laddr);
1.1.1.5 root 1961:
1.1.1.6 root 1962: // 命令がサブルーチンやトラップなどステップインできる命令か。
1963: if (md->IsOpStepIn(mem)) {
1.1.1.5 root 1964: // この次の命令にブレークをかける
1965: if (md->inst_bytes_fixed != 0) {
1966: // 固定長命令
1.1.1.6 root 1967: n_breakaddr = (uint32)mem.laddr;
1.1.1.5 root 1968: } else {
1969: // 可変長なら逆アセンブルしてみるしか
1970: std::string mnemonic;
1971: std::vector<uint8> v;
1972: // XXX 失敗したらどうすべ
1.1.1.6 root 1973: mem.ResetAddr(md->GetPC());
1974: md->Disassemble(mem, mnemonic, v);
1975: n_breakaddr = (uint32)mem.laddr;
1.1.1.5 root 1976: }
1.1 root 1977: } else {
1.1.1.5 root 1978: // そうでなければ1命令進める。
1979: // 0xffffffff は常にアドレスとして不正なのでこれをフラグに使う。
1980: n_breakaddr = 0xffffffff;
1.1 root 1981: }
1982: }
1983:
1.1.1.2 root 1984: // デバッガ終了コマンド
1985: void
1986: Debugger::cmd_q()
1987: {
1988: cons->Print("quit debugger console.\n");
1.1 root 1989: }
1990:
1.1.1.5 root 1991: // VM リセット
1992: void
1993: Debugger::cmd_reset()
1994: {
1995: gScheduler->RequestResetHard();
1996: }
1997:
1.1 root 1998: // モニター表示
1999: void
2000: Debugger::cmd_show()
2001: {
1.1.1.3 root 2002: if (args.size() != 2) {
1.1 root 2003: cons->Print("usage: show <monitor>\n");
2004: for (auto& d : gObjects) {
2005: if (d->logname != "?") {
2006: cons->Print(" %s\n", d->logname.c_str());
2007: }
2008: }
2009: return;
2010: }
2011:
1.1.1.3 root 2012: std::string name = args[1];
1.1 root 2013: for (auto& d : gObjects) {
2014: if (d->logname == name) {
1.1.1.3 root 2015: ShowMonitor(*d);
1.1 root 2016: return;
2017: }
2018: }
2019: // 一致しない
2020: cons->Print("show: no such objects\n");
2021: }
2022:
1.1.1.3 root 2023: // モニターを更新して表示。
2024: void
1.1.1.4 root 2025: Debugger::ShowMonitor(IMonitor& monitor)
1.1.1.3 root 2026: {
2027: // モニタを更新
1.1.1.4 root 2028: TextScreen ts;
2029:
2030: auto size = monitor.GetMonitorSize();
2031: ts.Init(size.width, size.height);
2032:
2033: monitor.MonitorUpdate(ts);
2034: ShowTextScreen(ts);
1.1.1.3 root 2035: }
2036:
2037: // テキストスクリーンを表示。
1.1 root 2038: void
1.1.1.3 root 2039: Debugger::ShowTextScreen(TextScreen& monitor)
1.1 root 2040: {
2041: char sbuf[1024]; // 適当
2042:
2043: // monitor 内部バッファから ShiftJIS バッファを作成。
2044: // その際属性もエスケープシーケンスで再現する。
2045: int col = monitor.GetCol();
2046: int row = monitor.GetRow();
2047: const uint16 *src = monitor.GetBuf();
2048:
2049: for (int y = 0; y < row; y++) {
2050: int sy = y * col;
2051: int sx;
2052: uint attr;
2053: char *d;
2054:
2055: memset(sbuf, 0, sizeof(sbuf));
2056: d = sbuf;
2057: attr = TA::Normal;
2058: for (sx = 0; sx < col; sx++) {
2059: uint a = src[sy + sx] & 0xff00;
2060: uint ch = src[sy + sx] & 0x00ff;
2061:
2062: // 属性の変わり目
2063: if (attr != a) {
2064: switch (a) {
2065: case TA::Normal:
2066: case TA::Off:
2067: *d++ = 0x1b;
2068: *d++ = '[';
2069: *d++ = 'm';
2070: break;
2071: case TA::On:
2072: *d++ = 0x1b;
2073: *d++ = '[';
2074: *d++ = '7';
2075: *d++ = 'm';
2076: break;
2077: case TA::Disable:
2078: *d++ = 0x1b;
2079: *d++ = '[';
2080: *d++ = '2';
2081: *d++ = 'm';
2082: break;
2083: case TA::Em:
2084: *d++ = 0x1b;
2085: *d++ = '[';
2086: *d++ = '1';
2087: *d++ = 'm';
2088: break;
2089: }
2090: attr = a;
2091: }
2092: *d++ = ch;
2093: }
1.1.1.3 root 2094: // 行の終わりで一旦属性を戻しておく
2095: if (attr != TA::Normal) {
2096: *d++ = 0x1b;
2097: *d++ = '[';
2098: *d++ = 'm';
2099: }
1.1 root 2100: *d = '\0';
2101:
2102: // XXX 日本語が使われてれば UTF-8 にしないといけないはず
2103:
2104: cons->Print("%s\n", sbuf);
2105: }
2106: }
2107:
1.1.1.5 root 2108: // ステップ実行
2109: // s [<count>?:1] (途中レジスタを表示しない)
1.1 root 2110: void
2111: Debugger::cmd_s()
2112: {
1.1.1.5 root 2113: cmd_s_common(false);
2114: }
2115:
2116: // ステップ実行
2117: // st [<count>?:1] (命令ごとにレジスタを表示する)
2118: void
2119: Debugger::cmd_st()
2120: {
2121: cmd_s_common(true);
2122: }
2123:
2124: // ステップ実行共通部分
2125: void
2126: Debugger::cmd_s_common(bool trace)
2127: {
2128: t_enable = trace;
2129:
1.1.1.3 root 2130: if (args.size() > 1) {
1.1 root 2131: int count;
1.1.1.3 root 2132: count = strtol(args[1].c_str(), NULL, 10);
1.1 root 2133: if (count < 1) {
2134: cons->Print(" invalid step count: %d\n", count);
2135: return;
2136: }
2137:
2138: s_count = count;
2139: } else {
2140: s_count = 1;
2141: }
2142: s_enable = true;
2143: }
2144:
1.1.1.5 root 2145: // ステップアウト (途中レジスタを表示しない)
1.1 root 2146: void
2147: Debugger::cmd_so()
2148: {
1.1.1.5 root 2149: cmd_so_common(false);
2150: }
2151:
2152: // ステップアウト (命令ごとにレジスタを表示する)
2153: void
2154: Debugger::cmd_sot()
2155: {
2156: cmd_so_common(true);
2157: }
2158:
2159: // ステップアウトの共通部分
2160: void
2161: Debugger::cmd_so_common(bool trace)
2162: {
2163: t_enable = trace;
2164:
1.1 root 2165: so_enable = true;
1.1.1.2 root 2166: md->SetStepOut();
1.1 root 2167: }
2168:
1.1.1.5 root 2169: // t は st の省略形。互換のため。
1.1 root 2170: void
2171: Debugger::cmd_t()
2172: {
1.1.1.5 root 2173: cmd_st();
1.1 root 2174: }
2175:
1.1.1.4 root 2176: // 引数で示されるプレフィックスなしの16進数値を返す。
2177: // 正しく変換できれば値を valp に格納して true を返す。
2178: // そうでなければ false を返す。
2179: bool
2180: Debugger::ParseVerbHex(const char *arg, uint32 *valp)
2181: {
2182: uint32 val;
2183: char *end;
2184:
2185: errno = 0;
2186: val = strtoul(arg, &end, 16);
2187: if (arg[0] == '\0' || end[0] != '\0') {
2188: return false;
2189: }
2190: if (errno == ERANGE) {
2191: return false;
2192: }
2193:
2194: *valp = val;
2195: return true;
2196: }
2197:
1.1 root 2198: // 引数で示されるアドレスを返す。
2199: // '%' で始まればレジスタ。
2200: // そうでなければ16進数値。
2201: // アドレスが正しく取得できればアドレスを addr に格納し真を返す。
2202: // そうでなければエラーメッセージを表示して偽を返す。
2203: bool
1.1.1.4 root 2204: Debugger::ParseAddr(const char *arg, uint32 *addrp)
1.1 root 2205: {
1.1.1.4 root 2206: uint32 addr;
1.1 root 2207: char *end;
2208:
2209: if (arg[0] == '%') {
1.1.1.2 root 2210: // '%' から始まればレジスタ
2211: uint64 r;
2212: r = md->GetRegAddr(arg + 1);
2213: if ((int64)r < 0) {
2214: #if 0 // not yet
1.1 root 2215: cons->Print("valid register name are: %s%s\n",
2216: "%d0-%d7,%a0-%a7,%sp,%usp,%isp,%pc",
2217: ",%msp,%vbr,%srp,%crp");
1.1.1.2 root 2218: #endif
2219: cons->Print("invalid register name\n");
1.1 root 2220: return false;
2221: }
1.1.1.2 root 2222: addr = (uint32)r;
1.1 root 2223: } else {
2224: /* そうでなければ番地指定 */
2225: errno = 0;
2226: addr = strtoul(arg, &end, 16);
2227: if (arg[0] == '\0' || end[0] != '\0') {
2228: cons->Print("invalid address: '%s'\n", arg);
2229: return false;
2230: }
2231: if (errno == ERANGE) {
2232: cons->Print("out of range: %s\n", arg);
2233: return false;
2234: }
2235: }
2236:
2237: *addrp = addr;
2238: return true;
2239: }
2240:
1.1.1.4 root 2241:
2242: //
1.1.1.5 root 2243: // MD
2244: //
2245:
1.1.1.6 root 2246: // デストラクタ
2247: DebuggerMD::~DebuggerMD()
2248: {
1.1.1.5 root 2249: }
2250:
2251:
2252: //
1.1.1.4 root 2253: // ブレークポイントモニター
2254: //
2255:
2256: // コンストラクタ
2257: BreakpointMonitor::BreakpointMonitor()
2258: {
2259: monitor_size = nnSize(55, 9);
2260: }
2261:
2262: void
2263: BreakpointMonitor::MonitorUpdate(TextScreen& monitor)
2264: {
2265: gDebugger->MonitorBreakpoint(monitor);
2266: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.