/* handle_gdb_valgrind_command handles the provided mon string command. If command is recognised, return 1 else return 0. Note that in case of ambiguous command, 1 is returned. *sink_wanted_at_return is modified if one of the commands 'v.set *_output' is handled. */ static int handle_gdb_valgrind_command (char* mon, OutputSink* sink_wanted_at_return) { UWord ret = 0; char s[strlen(mon)+1]; /* copy for strtok_r */ char* wcmd; Char* ssaveptr; char* endptr; int kwdid; int int_value; vg_assert (initial_valgrind_sink_saved); strcpy (s, mon); wcmd = strtok_r (s, " ", &ssaveptr); /* NB: if possible, avoid introducing a new command below which starts with the same 3 first letters as an already existing command. This ensures a shorter abbreviation for the user. */ switch (VG_(keyword_id) ("help v.set v.info v.wait v.kill v.translate", wcmd, kwd_report_duplicated_matches)) { case -2: ret = 1; break; case -1: break; case 0: /* help */ ret = 1; wcmd = strtok_r (NULL, " ", &ssaveptr); if (wcmd == NULL) { int_value = 0; } else { switch (VG_(keyword_id) ("debug", wcmd, kwd_report_all)) { case -2: int_value = 0; break; case -1: int_value = 0; break; case 0: int_value = 1; break; default: tl_assert (0); } } VG_(gdb_printf) ( "general valgrind monitor commands:\n" " help [debug] : monitor command help. With debug: + debugging commands\n" " v.wait [<ms>] : sleep <ms> (default 0) then continue\n" " v.info all_errors : show all errors found so far\n" " v.info last_error : show last error found\n" " v.info n_errs_found : show the nr of errors found so far\n" " v.kill : kill the Valgrind process\n" " v.set gdb_output : set valgrind output to gdb\n" " v.set log_output : set valgrind output to log\n" " v.set mixed_output : set valgrind output to log, interactive output to gdb\n" " v.set vgdb-error <errornr> : debug me at error >= <errornr> \n"); if (int_value) { VG_(gdb_printf) ( "debugging valgrind internals monitor commands:\n" " v.info gdbserver_status : show gdbserver status\n" " v.info memory [aspacemgr] : show valgrind heap memory stats\n" " (with aspacemgr arg, also shows valgrind segments on log ouput)\n" " v.info scheduler : show valgrind thread state and stacktrace\n" " v.set debuglog <level> : set valgrind debug log level to <level>\n" " v.translate <addr> [<traceflags>] : debug translation of <addr> with <traceflags>\n" " (default traceflags 0b00100000 : show after instrumentation)\n" " An additional flag 0b100000000 allows to show gdbserver instrumentation\n"); } break; case 1: /* v.set */ ret = 1; wcmd = strtok_r (NULL, " ", &ssaveptr); switch (kwdid = VG_(keyword_id) ("vgdb-error debuglog gdb_output log_output mixed_output", wcmd, kwd_report_all)) { case -2: case -1: break; case 0: /* vgdb-error */ case 1: /* debuglog */ wcmd = strtok_r (NULL, " ", &ssaveptr); if (wcmd == NULL) { int_value = 0; endptr = "empty"; /* to report an error below */ } else { int_value = strtol (wcmd, &endptr, 10); } if (*endptr != '\0') { VG_(gdb_printf) ("missing or malformed integer value\n"); } else if (kwdid == 0) { VG_(gdb_printf) ("vgdb-error value changed from %d to %d\n", VG_(dyn_vgdb_error), int_value); VG_(dyn_vgdb_error) = int_value; } else if (kwdid == 1) { VG_(gdb_printf) ("debuglog value changed from %d to %d\n", VG_(debugLog_getLevel)(), int_value); VG_(debugLog_startup) (int_value, "gdbsrv"); } else { vg_assert (0); } break; case 2: /* gdb_output */ (*sink_wanted_at_return).fd = -2; command_output_to_log = False; VG_(gdb_printf) ("valgrind output will go to gdb\n"); break; case 3: /* log_output */ (*sink_wanted_at_return).fd = initial_valgrind_sink.fd; command_output_to_log = True; VG_(gdb_printf) ("valgrind output will go to log\n"); break; case 4: /* mixed output */ (*sink_wanted_at_return).fd = initial_valgrind_sink.fd; command_output_to_log = False; VG_(gdb_printf) ("valgrind output will go to log, interactive output will go to gdb\n"); break; default: vg_assert (0); } break; case 2: /* v.info */ { ret = 1; wcmd = strtok_r (NULL, " ", &ssaveptr); switch (kwdid = VG_(keyword_id) ("all_errors n_errs_found last_error gdbserver_status memory" " scheduler", wcmd, kwd_report_all)) { case -2: case -1: break; case 0: // all_errors // A verbosity of minimum 2 is needed to show the errors. VG_(show_all_errors)(/* verbosity */ 2, /* xml */ False); break; case 1: // n_errs_found VG_(gdb_printf) ("n_errs_found %d n_errs_shown %d (vgdb-error %d)\n", VG_(get_n_errs_found) (), VG_(get_n_errs_shown) (), VG_(dyn_vgdb_error)); break; case 2: // last_error VG_(show_last_error)(); break; case 3: // gdbserver_status VG_(gdbserver_status_output)(); break; case 4: /* memory */ VG_(print_all_arena_stats) (); if (VG_(clo_profile_heap)) VG_(print_arena_cc_analysis) (); wcmd = strtok_r (NULL, " ", &ssaveptr); if (wcmd != NULL) { switch (VG_(keyword_id) ("aspacemgr", wcmd, kwd_report_all)) { case -2: case -1: break; case 0: VG_(am_show_nsegments) (0, "gdbserver v.info memory aspacemgr"); break; default: tl_assert (0); } } ret = 1; break; case 5: /* scheduler */ VG_(show_sched_status) (); ret = 1; break; default: vg_assert(0); } break; } case 3: /* v.wait */ wcmd = strtok_r (NULL, " ", &ssaveptr); if (wcmd != NULL) { int_value = strtol (wcmd, &endptr, 10); VG_(gdb_printf) ("gdbserver: continuing in %d ms ...\n", int_value); VG_(poll)(NULL, 0, int_value); } VG_(gdb_printf) ("gdbserver: continuing after wait ...\n"); ret = 1; break; case 4: /* v.kill */ kill_request ("monitor command request to kill this process\n"); break; case 5: { /* v.translate */ Addr address; SizeT verbosity = 0x20; ret = 1; VG_(strtok_get_address_and_size) (&address, &verbosity, &ssaveptr); if (address != (Addr) 0 || verbosity != 0) { /* we need to force the output to log for the translation trace, as low level VEX tracing cannot be redirected to gdb. */ int saved_command_output_to_log = command_output_to_log; int saved_fd = VG_(log_output_sink).fd; Bool single_stepping_on_entry = valgrind_single_stepping(); int vex_verbosity = verbosity & 0xff; VG_(log_output_sink).fd = initial_valgrind_sink.fd; if ((verbosity & 0x100) && !single_stepping_on_entry) { valgrind_set_single_stepping(True); // to force gdbserver instrumentation. } # if defined(VGA_arm) // on arm, we need to (potentially) convert this address // to the thumb form. address = thumb_pc (address); # endif VG_(translate) ( 0 /* dummy ThreadId; irrelevant due to debugging*/, address, /*debugging*/True, (Int) vex_verbosity, /*bbs_done*/0, /*allow redir?*/True); if ((verbosity & 0x100) && !single_stepping_on_entry) { valgrind_set_single_stepping(False); // reset single stepping. } command_output_to_log = saved_command_output_to_log; VG_(log_output_sink).fd = saved_fd; } break; } default: vg_assert (0); } return ret; }
Bool VG_(is_watched)(PointKind kind, Addr addr, Int szB) { GS_Watch* g; Bool watched = False; const ThreadId tid = VG_(running_tid); if (!gdbserver_called) return False; Addr to = addr + szB; // semi-open interval [addr, to[ vg_assert (kind == access_watchpoint || kind == read_watchpoint || kind == write_watchpoint); dlog(1, "tid %d VG_(is_watched) %s addr %p szB %d\n", tid, VG_(ppPointKind) (kind), C2v(addr), szB); VG_(HT_ResetIter) (gs_watches); while ((g = VG_(HT_Next) (gs_watches))) { switch (g->kind) { case software_breakpoint: case hardware_breakpoint: break; case access_watchpoint: case read_watchpoint: case write_watchpoint: if (to <= g->addr || addr >= (g->addr + g->len)) /* If no overlap, examine next watchpoint: */ continue; watched = True; /* We have an overlap */ /* call gdbserver if access kind reported by the tool matches the watchpoint kind. */ if (kind == access_watchpoint || g->kind == access_watchpoint || g->kind == kind) { /* Watchpoint encountered. If this is a read watchpoint, we directly call gdbserver to report it to gdb. Otherwise, for a write watchpoint, we have to finish the instruction so as to modify the value. If we do not finish the instruction, then gdb sees no value change and continues. For a read watchpoint, we better call gdbserver directly: in case the current block is not gdbserved, Valgrind will execute instructions till the next block. */ /* set the watchpoint stop address to the first read or written. */ if (g->addr <= addr) { VG_(set_watchpoint_stop_address) (addr); } else { VG_(set_watchpoint_stop_address) (g->addr); } if (kind == write_watchpoint) { /* Let Valgrind stop as early as possible after this instruction by switching to Single Stepping mode. */ valgrind_set_single_stepping (True); invalidate_current_ip (tid, "m_gdbserver write watchpoint"); } else { call_gdbserver (tid, watch_reason); VG_(set_watchpoint_stop_address) ((Addr) 0); } return True; // we are watched here. } break; default: vg_assert (0); } } return watched; }