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