Example #1
0
static void gen_suppression(Error* err)
{
   ExeContext* ec      = VG_(get_error_where)(err);
   Int         stop_at = VG_(clo_backtrace_size);

   /* At most VG_MAX_SUPP_CALLERS names */
   if (stop_at > VG_MAX_SUPP_CALLERS) stop_at = VG_MAX_SUPP_CALLERS;
   vg_assert(stop_at > 0);

      //(example code, see comment on CoreSuppKind above)
   if (0) {    
   //if (0) ThreadErr == err->ekind) {
   //   VG_(printf)("{\n");
   //   VG_(printf)("   <insert a suppression name here>\n");
   //   VG_(printf)("   core:Thread\n");

   } else {
      Char* name = VG_TDICT_CALL(tool_get_error_name, err);
      if (NULL == name) {
         VG_(message)(Vg_UserMsg, 
                      "(%s does not allow error to be suppressed)",
                      VG_(details).name);
         return;
      }
      VG_(printf)("{\n");
      VG_(printf)("   <insert a suppression name here>\n");
      VG_(printf)("   %s:%s\n", VG_(details).name, name);
      VG_TDICT_CALL(tool_print_extra_suppression_info, err);
   }

   // Print stack trace elements
   VG_(apply_StackTrace)(printSuppForIp, VG_(extract_StackTrace)(ec), stop_at);

   VG_(printf)("}\n");
}
Example #2
0
void VG_(print_all_stats) (Bool memory_stats, Bool tool_stats)
{
   if (memory_stats) {
      VG_(message)(Vg_DebugMsg, "\n");
      VG_(message)(Vg_DebugMsg, 
         "------ Valgrind's internal memory use stats follow ------\n" );
      VG_(sanity_check_malloc_all)();
       VG_(message)
          (Vg_DebugMsg,
           "------ %'13llu bytes have already been mmap-ed ANONYMOUS.\n",
           VG_(am_get_anonsize_total)());
      VG_(print_all_arena_stats)();
      if (VG_(clo_profile_heap))
         VG_(print_arena_cc_analysis) ();
      VG_(message)(Vg_DebugMsg, "\n");
   }

   VG_(print_translation_stats)();
   VG_(print_tt_tc_stats)();
   VG_(print_scheduler_stats)();
   VG_(print_ExeContext_stats)( False /* with_stacktraces */ );
   VG_(print_errormgr_stats)();
   if (tool_stats && VG_(needs).print_stats) {
      VG_TDICT_CALL(tool_print_stats);
   }
}
Example #3
0
/* handle_gdb_monitor_command handles the provided mon string command,
   which can be either a "standard" valgrind monitor command
   or a tool specific monitor command.
   If command recognised, return 1 else return 0.
   Note that in case of ambiguous command, 1 is returned.
*/
static
int handle_gdb_monitor_command (char *mon)
{
   UWord ret = 0;
   UWord tool_ret = 0;
   // initially, we assume that when returning, the desired sink is the
   // one we have when entering. It can however be changed by the standard
   // valgrind command handling.
   OutputSink sink_wanted_at_return = VG_(log_output_sink);
   // When using gdbserver, we temporarily disable xml output.
   Bool save_clo_xml = VG_(clo_xml);
   VG_(clo_xml) = False;

   if (!initial_valgrind_sink_saved) {
      /* first time we enter here, we save the valgrind default log sink */
      initial_valgrind_sink = sink_wanted_at_return;
      initial_valgrind_sink_saved = True;
   }

   if (!command_output_to_log)
      VG_(log_output_sink).fd = -2; /* redirect to monitor_output */

   ret = handle_gdb_valgrind_command (mon, &sink_wanted_at_return);

   /* Even if command was recognised by valgrind core, we call the
      tool command handler : this is needed to handle help command
      and/or to let the tool do some additional processing of a
      valgrind standard command. Note however that if valgrind
      recognised the command, we will always return success. */
   if (VG_(needs).client_requests) {
      /* If the tool reports an error when handling a monitor command,
         we need to avoid calling gdbserver during this command
         handling. So, we temporarily set VG_(dyn_vgdb_error) to
         a huge value to ensure m_errormgr.c does not call gdbserver. */
      Int save_dyn_vgdb_error = VG_(dyn_vgdb_error);
      UWord arg[2];
      VG_(dyn_vgdb_error) = 999999999;
      arg[0] = (UWord) VG_USERREQ__GDB_MONITOR_COMMAND;
      arg[1] = (UWord) mon;
      VG_TDICT_CALL(tool_handle_client_request, VG_(running_tid), arg,
                    &tool_ret);
      VG_(dyn_vgdb_error) = save_dyn_vgdb_error;
   }

   VG_(message_flush) ();

   /* restore or set the desired output */
   VG_(log_output_sink).fd = sink_wanted_at_return.fd;
   VG_(clo_xml) = save_clo_xml;

   if (ret | tool_ret)
      return 1;
   else
      return 0;
}
Example #4
0
static
Bool supp_matches_error(Supp* su, Error* err)
{
   switch (su->skind) {
      case PThreadSupp:
         return (err->ekind == ThreadErr || err->ekind == MutexErr);
      default:
         if (VG_(needs).tool_errors) {
            return VG_TDICT_CALL(tool_error_matches_suppression, err, su);
         } else {
            VG_(printf)(
               "\nUnhandled suppression type: %u.  VG_(needs).tool_errors\n"
               "probably needs to be set.\n",
               err->ekind);
            VG_(tool_panic)("unhandled suppression type");
         }
   }
}
Example #5
0
static
Bool supp_matches_error(Supp* su, Error* err)
{
   switch (su->skind) {
      //(example code, see comment on CoreSuppKind above)
      //case ThreadSupp:
      //   return (err->ekind == ThreadErr);
      default:
         if (VG_(needs).tool_errors) {
            return VG_TDICT_CALL(tool_error_matches_suppression, err, su);
         } else {
            VG_(printf)(
               "\nUnhandled suppression type: %u.  VG_(needs).tool_errors\n"
               "probably needs to be set.\n",
               err->ekind);
            VG_(tool_panic)("unhandled suppression type");
         }
   }
}
Example #6
0
/* Second top-level entry point to the error management subsystem, for
   errors that the tool wants to report immediately, eg. because they're
   guaranteed to only happen once.  This avoids all the recording and
   comparing stuff.  But they can be suppressed;  returns True if it is
   suppressed.  Bool 'print_error' dictates whether to print the error. 
   Bool 'count_error' dictates whether to count the error in n_errs_found.
*/
Bool VG_(unique_error) ( ThreadId tid, ErrorKind ekind, Addr a, Char* s,
                         void* extra, ExeContext* where, Bool print_error,
                         Bool allow_db_attach, Bool count_error )
{
   Error err;
   Supp *su;

   /* Build ourselves the error */
   construct_error ( &err, tid, ekind, a, s, extra, where );

   /* Unless it's suppressed, we're going to show it.  Don't need to make
      a copy, because it's only temporary anyway.

      Then update the 'extra' part with VG_(tdict).tool_update_extra),
      because that can have an affect on whether it's suppressed.  Ignore
      the size return value of VG_(tdict).tool_update_extra, because we're
      not copying 'extra'. */
   (void)VG_TDICT_CALL(tool_update_extra, &err);

   su = is_suppressible_error(&err);
   if (NULL == su) {
      if (count_error)
         n_errs_found++;

      if (print_error) {
         if (!is_first_shown_context)
            VG_(message)(Vg_UserMsg, "");
         pp_Error(&err);
         is_first_shown_context = False;
         n_errs_shown++;
         do_actions_on_error(&err, allow_db_attach);
      }
      return False;

   } else {
      n_errs_suppressed++;
      su->count++;
      return True;
   }
}
Example #7
0
/* Compare errors, to detect duplicates. */
static Bool eq_Error ( VgRes res, Error* e1, Error* e2 )
{
   if (e1->ekind != e2->ekind) 
      return False;
   if (!VG_(eq_ExeContext)(res, e1->where, e2->where))
      return False;

   switch (e1->ekind) {
      //(example code, see comment on CoreSuppKind above)
      //case ThreadErr:
      //   vg_assert(VG_(needs).core_errors);
      //   return <something>
      default: 
         if (VG_(needs).tool_errors) {
            return VG_TDICT_CALL(tool_eq_Error, res, e1, e2);
         } else {
            VG_(printf)("\nUnhandled error type: %u. VG_(needs).tool_errors\n"
                        "probably needs to be set.\n",
                        e1->ekind);
            VG_(tool_panic)("unhandled error type");
         }
   }
}
Example #8
0
/* Compare error contexts, to detect duplicates.  Note that if they
   are otherwise the same, the faulting addrs and associated rwoffsets
   are allowed to be different.  */
static Bool eq_Error ( VgRes res, Error* e1, Error* e2 )
{
   if (e1->ekind != e2->ekind) 
      return False;
   if (!VG_(eq_ExeContext)(res, e1->where, e2->where))
      return False;

   switch (e1->ekind) {
     //      case ThreadErr:
     //      case MutexErr:
     //         vg_assert(VG_(needs).core_errors);
     //         return VG_(tm_error_equal)(res, e1, e2);
      default: 
         if (VG_(needs).tool_errors) {
            return VG_TDICT_CALL(tool_eq_Error, res, e1, e2);
         } else {
            VG_(printf)("\nUnhandled error type: %u. VG_(needs).tool_errors\n"
                        "probably needs to be set.\n",
                        e1->ekind);
            VG_(tool_panic)("unhandled error type");
         }
   }
}
Example #9
0
static void pp_Error ( Error* err )
{
   if (VG_(clo_xml)) {
      VG_(message)(Vg_UserMsg, "<error>");
      VG_(message)(Vg_UserMsg, "  <unique>0x%x</unique>",
                                  err->unique);
      VG_(message)(Vg_UserMsg, "  <tid>%d</tid>", err->tid);
   }

   if (!VG_(clo_xml)) {
     if (VG_(tdict).tool_show_ThreadIDs_for_errors
         && err->tid > 0 && err->tid != last_tid_printed) {
         VG_(message)(Vg_UserMsg, "Thread %d:", err->tid );
         last_tid_printed = err->tid;
      }
   }

   switch (err->ekind) {
      //(example code, see comment on CoreSuppKind above)
      //case ThreadErr:
      //   vg_assert(VG_(needs).core_errors);
      //   VG_(tm_error_print)(err);
      //   break;
      default: 
         if (VG_(needs).tool_errors)
            VG_TDICT_CALL( tool_pp_Error, err );
         else {
            VG_(printf)("\nUnhandled error type: %u.  VG_(needs).tool_errors\n"
                        "probably needs to be set?\n",
                        err->ekind);
            VG_(tool_panic)("unhandled error type");
         }
   }

   if (VG_(clo_xml))
      VG_(message)(Vg_UserMsg, "</error>");
}
Example #10
0
/* Read suppressions from the file specified in VG_(clo_suppressions)
   and place them in the suppressions list.  If there's any difficulty
   doing this, just give up -- there's no point in trying to recover.  
*/
static void load_one_suppressions_file ( Char* filename )
{
#  define N_BUF 200
   SysRes sres;
   Int    fd, i;
   Bool   eof;
   Char   buf[N_BUF+1];
   Char*  tool_names;
   Char*  supp_name;
   Char*  err_str = NULL;
   SuppLoc tmp_callers[VG_MAX_SUPP_CALLERS];

   fd   = -1;
   sres = VG_(open)( filename, VKI_O_RDONLY, 0 );
   if (sres.isError) {
      if (VG_(clo_xml))
         VG_(message)(Vg_UserMsg, "</valgrindoutput>\n");
      VG_(message)(Vg_UserMsg, "FATAL: can't open suppressions file '%s'", 
                   filename );
      VG_(exit)(1);
   }
   fd = sres.res;

#  define BOMB(S)  { err_str = S;  goto syntax_error; }

   while (True) {
      /* Assign and initialise the two suppression halves (core and tool) */
      Supp* supp;
      supp        = VG_(arena_malloc)(VG_AR_CORE, sizeof(Supp));
      supp->count = 0;

      // Initialise temporary reading-in buffer.
      for (i = 0; i < VG_MAX_SUPP_CALLERS; i++) {
         tmp_callers[i].ty   = NoName;
         tmp_callers[i].name = NULL;
      }

      supp->string = supp->extra = NULL;

      eof = VG_(get_line) ( fd, buf, N_BUF );
      if (eof) break;

      if (!VG_STREQ(buf, "{")) BOMB("expected '{' or end-of-file");
      
      eof = VG_(get_line) ( fd, buf, N_BUF );

      if (eof || VG_STREQ(buf, "}")) BOMB("unexpected '}'");

      supp->sname = VG_(arena_strdup)(VG_AR_CORE, buf);

      eof = VG_(get_line) ( fd, buf, N_BUF );

      if (eof) BOMB("unexpected end-of-file");

      /* Check it has the "tool1,tool2,...:supp" form (look for ':') */
      i = 0;
      while (True) {
         if (buf[i] == ':')  break;
         if (buf[i] == '\0') BOMB("malformed 'tool1,tool2,...:supp' line");
         i++;
      }
      buf[i]    = '\0';    /* Replace ':', splitting into two strings */

      tool_names = & buf[0];
      supp_name  = & buf[i+1];

      if (VG_(needs).core_errors && tool_name_present("core", tool_names))
      {
         // A core suppression
         //(example code, see comment on CoreSuppKind above)
         //if (VG_STREQ(supp_name, "Thread"))
         //   supp->skind = ThreadSupp;
         //else
            BOMB("unknown core suppression type");
      }
      else if (VG_(needs).tool_errors && 
               tool_name_present(VG_(details).name, tool_names))
      {
         // A tool suppression
         if (VG_TDICT_CALL(tool_recognised_suppression, supp_name, supp)) {
            /* Do nothing, function fills in supp->skind */
         } else {
            BOMB("unknown tool suppression type");
         }
      }
      else {
         // Ignore rest of suppression
         while (True) {
            eof = VG_(get_line) ( fd, buf, N_BUF );
            if (eof) BOMB("unexpected end-of-file");
            if (VG_STREQ(buf, "}"))
               break;
         }
         continue;
      }

      if (VG_(needs).tool_errors && 
          !VG_TDICT_CALL(tool_read_extra_suppression_info, fd, buf, N_BUF, supp))
      {
         BOMB("bad or missing extra suppression info");
      }

      i = 0;
      while (True) {
         eof = VG_(get_line) ( fd, buf, N_BUF );
         if (eof)
            BOMB("unexpected end-of-file");
         if (VG_STREQ(buf, "}")) {
            if (i > 0) {
               break;
            } else {
               BOMB("missing stack trace");
            }
         }
         if (i == VG_MAX_SUPP_CALLERS)
            BOMB("too many callers in stack trace");
         if (i > 0 && i >= VG_(clo_backtrace_size)) 
            break;
         tmp_callers[i].name = VG_(arena_strdup)(VG_AR_CORE, buf);
         if (!setLocationTy(&(tmp_callers[i])))
            BOMB("location should start with 'fun:' or 'obj:'");
         i++;
      }

      // If the num callers is >= VG_(clo_backtrace_size), ignore any extra
      // lines and grab the '}'.
      if (!VG_STREQ(buf, "}")) {
         do {
            eof = VG_(get_line) ( fd, buf, N_BUF );
         } while (!eof && !VG_STREQ(buf, "}"));
      }

      // Copy tmp_callers[] into supp->callers[]
      supp->n_callers = i;
      supp->callers = VG_(arena_malloc)(VG_AR_CORE, i*sizeof(SuppLoc));
      for (i = 0; i < supp->n_callers; i++) {
         supp->callers[i] = tmp_callers[i];
      }

      supp->next = suppressions;
      suppressions = supp;
   }
   VG_(close)(fd);
   return;

  syntax_error:
   if (VG_(clo_xml))
      VG_(message)(Vg_UserMsg, "</valgrindoutput>\n");
   VG_(message)(Vg_UserMsg, 
                "FATAL: in suppressions file '%s': %s", filename, err_str );
   
   VG_(close)(fd);
   VG_(message)(Vg_UserMsg, "exiting now.");
   VG_(exit)(1);

#  undef BOMB
#  undef N_BUF   
}
Example #11
0
/* Top-level entry point to the error management subsystem.
   All detected errors are notified here; this routine decides if/when the
   user should see the error. */
void VG_(maybe_record_error) ( ThreadId tid, 
                               ErrorKind ekind, Addr a, Char* s, void* extra )
{
          Error  err;
          Error* p;
          Error* p_prev;
          UInt   extra_size;
          VgRes  exe_res          = Vg_MedRes;
   static Bool   stopping_message = False;
   static Bool   slowdown_message = False;

   /* After M_COLLECT_NO_ERRORS_AFTER_SHOWN different errors have
      been found, or M_COLLECT_NO_ERRORS_AFTER_FOUND total errors
      have been found, just refuse to collect any more.  This stops
      the burden of the error-management system becoming excessive in
      extremely buggy programs, although it does make it pretty
      pointless to continue the Valgrind run after this point. */
   if (VG_(clo_error_limit) 
       && (n_errs_shown >= M_COLLECT_NO_ERRORS_AFTER_SHOWN
           || n_errs_found >= M_COLLECT_NO_ERRORS_AFTER_FOUND)
       && !VG_(clo_xml)) {
      if (!stopping_message) {
         VG_(message)(Vg_UserMsg, "");

	 if (n_errs_shown >= M_COLLECT_NO_ERRORS_AFTER_SHOWN) {
            VG_(message)(Vg_UserMsg, 
               "More than %d different errors detected.  "
               "I'm not reporting any more.",
               M_COLLECT_NO_ERRORS_AFTER_SHOWN );
         } else {
            VG_(message)(Vg_UserMsg, 
               "More than %d total errors detected.  "
               "I'm not reporting any more.",
               M_COLLECT_NO_ERRORS_AFTER_FOUND );
	 }

         VG_(message)(Vg_UserMsg, 
            "Final error counts will be inaccurate.  Go fix your program!");
         VG_(message)(Vg_UserMsg, 
            "Rerun with --error-limit=no to disable this cutoff.  Note");
         VG_(message)(Vg_UserMsg, 
            "that errors may occur in your program without prior warning from");
         VG_(message)(Vg_UserMsg, 
            "Valgrind, because errors are no longer being displayed.");
         VG_(message)(Vg_UserMsg, "");
         stopping_message = True;
      }
      return;
   }

   /* After M_COLLECT_ERRORS_SLOWLY_AFTER different errors have
      been found, be much more conservative about collecting new
      ones. */
   if (n_errs_shown >= M_COLLECT_ERRORS_SLOWLY_AFTER
       && !VG_(clo_xml)) {
      exe_res = Vg_LowRes;
      if (!slowdown_message) {
         VG_(message)(Vg_UserMsg, "");
         VG_(message)(Vg_UserMsg, 
            "More than %d errors detected.  Subsequent errors",
            M_COLLECT_ERRORS_SLOWLY_AFTER);
         VG_(message)(Vg_UserMsg, 
            "will still be recorded, but in less detail than before.");
         slowdown_message = True;
      }
   }

   /* Build ourselves the error */
   construct_error ( &err, tid, ekind, a, s, extra, NULL );

   /* First, see if we've got an error record matching this one. */
   em_errlist_searches++;
   p       = errors;
   p_prev  = NULL;
   while (p != NULL) {
      em_errlist_cmps++;
      if (eq_Error(exe_res, p, &err)) {
         /* Found it. */
         p->count++;
	 if (p->supp != NULL) {
            /* Deal correctly with suppressed errors. */
            p->supp->count++;
            n_errs_suppressed++;	 
         } else {
            n_errs_found++;
         }

         /* Move p to the front of the list so that future searches
            for it are faster. */
         if (p_prev != NULL) {
            vg_assert(p_prev->next == p);
            p_prev->next = p->next;
            p->next      = errors;
            errors       = p;
	 }

         return;
      }
      p_prev = p;
      p      = p->next;
   }

   /* Didn't see it.  Copy and add. */

   /* OK, we're really going to collect it.  The context is on the stack and
      will disappear shortly, so we must copy it.  First do the main
      (non-'extra') part.
     
      Then VG_(tdict).tool_update_extra can update the 'extra' part.  This
      is for when there are more details to fill in which take time to work
      out but don't affect our earlier decision to include the error -- by
      postponing those details until now, we avoid the extra work in the
      case where we ignore the error.  Ugly.

      Then, if there is an 'extra' part, copy it too, using the size that
      VG_(tdict).tool_update_extra returned.  Also allow for people using
      the void* extra field for a scalar value like an integer.
   */

   /* copy main part */
   p = VG_(arena_malloc)(VG_AR_ERRORS, sizeof(Error));
   *p = err;

   /* update 'extra' */
   switch (ekind) {
      //(example code, see comment on CoreSuppKind above)
      //case ThreadErr:
      //   vg_assert(VG_(needs).core_errors);
      //   extra_size = <something>
      //   break;
      default:
         vg_assert(VG_(needs).tool_errors);
         extra_size = VG_TDICT_CALL(tool_update_extra, p);
         break;
   }

   /* copy block pointed to by 'extra', if there is one */
   if (NULL != p->extra && 0 != extra_size) { 
      void* new_extra = VG_(malloc)(extra_size);
      VG_(memcpy)(new_extra, p->extra, extra_size);
      p->extra = new_extra;
   }

   p->next = errors;
   p->supp = is_suppressible_error(&err);
   errors  = p;
   if (p->supp == NULL) {
      n_errs_found++;
      if (!is_first_shown_context)
         VG_(message)(Vg_UserMsg, "");
      pp_Error(p);
      is_first_shown_context = False;
      n_errs_shown++;
      do_actions_on_error(p, /*allow_db_attach*/True);
   } else {
      n_errs_suppressed++;
      p->supp->count++;
   }
}
Example #12
0
/* 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;
   HChar *ssaveptr;
   const 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"
                            " v.do",
                            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: vg_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 location <addr>  : show information about location <addr>\n"
"  v.info n_errs_found [msg] : show the nr of errors found so far and the given msg\n"
"  v.info open_fds         : show open file descriptors (only if --track-fds=yes)\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 merge-recursive-frames <num> : merge recursive calls in max <num> frames\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.do   expensive_sanity_check_general : do an expensive sanity check now\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 exectxt          : show stacktraces and stats of all execontexts\n"
"  v.info scheduler        : show valgrind thread state and stacktrace\n"
"  v.info stats            : show various valgrind and tool stats\n"
"  v.info unwind <addr> [<len>] : show unwind debug info for <addr> .. <addr+len>\n"
"  v.set debuglog <level>  : set valgrind debug log level to <level>\n"
"  v.set hostvisibility [yes*|no] : (en/dis)ables access by gdb/gdbserver to\n"
"    Valgrind internal host status/memory\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 merge-recursive-frames"
               " gdb_output log_output mixed_output hostvisibility",
               wcmd, kwd_report_all)) {
      case -2:
      case -1: 
         break;
      case 0: /* vgdb-error */
      case 1: /* debuglog */
      case 2: /* merge-recursive-frames */
         wcmd = strtok_r (NULL, " ", &ssaveptr);
         if (wcmd == NULL) {
            int_value = 0;
            endptr = "empty"; /* to report an error below */
         } else {
            HChar *the_end;
            int_value = strtol (wcmd, &the_end, 10);
            endptr = the_end;
         }
         if (*endptr != '\0') {
            VG_(gdb_printf) ("missing or malformed integer value\n");
         } else if (kwdid == 0) {
            VG_(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_(printf) ("debuglog value changed from %d to %d\n",
                             VG_(debugLog_getLevel)(), int_value);
            VG_(debugLog_startup) (int_value, "gdbsrv");
         } else if (kwdid == 2) {
            VG_(printf)
               ("merge-recursive-frames value changed from %d to %d\n",
                VG_(clo_merge_recursive_frames), int_value);
            VG_(clo_merge_recursive_frames) = int_value;
         } else {
            vg_assert (0);
         }
         break;
      case 3: /* 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 4: /* 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 5: /* 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;
      case 6: /* hostvisibility */
         wcmd = strtok_r (NULL, " ", &ssaveptr);
         if (wcmd != NULL) {
            switch (VG_(keyword_id) ("yes no", wcmd, kwd_report_all)) {
            case -2:
            case -1: break;
            case  0: 
               hostvisibility = True;
               break;
            case 1:
               hostvisibility = False;
               break;
            default: vg_assert (0);
            }
         } else {
            hostvisibility = True;
         }
         if (hostvisibility) {
            const DebugInfo *tooldi 
               = VG_(find_DebugInfo) ((Addr)handle_gdb_valgrind_command);
            /* Normally, we should always find the tooldi. In case we
               do not, suggest a 'likely somewhat working' address: */
            const Addr tool_text_start
               = tooldi ?
               VG_(DebugInfo_get_text_avma) (tooldi) : 0x38000000;
            const NSegment *toolseg
               = tooldi ?
                 VG_(am_find_nsegment) (VG_(DebugInfo_get_text_avma) (tooldi))
                 : NULL;
            VG_(gdb_printf) 
               ("Enabled access to Valgrind memory/status by GDB\n"
                "If not yet done, tell GDB which valgrind file(s) to use, "
                "typically:\n"
                "add-symbol-file %s %p\n", 
                toolseg ? VG_(am_get_filename)(toolseg)
                : "<toolfile> <address> e.g.",
                (void*)tool_text_start);
         } else
            VG_(gdb_printf)
               ("Disabled access to Valgrind memory/status by 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 stats open_fds exectxt location unwind",
               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_(printf) ("n_errs_found %d n_errs_shown %d (vgdb-error %d) %s\n",
                      VG_(get_n_errs_found) (),
                      VG_(get_n_errs_shown) (),
                      VG_(dyn_vgdb_error),
                      wordn (mon, 3));
         break;
      case 2: // last_error
         VG_(show_last_error)();
         break;
      case  3: // gdbserver_status
         VG_(gdbserver_status_output)();
         break;
      case  4: /* memory */
         VG_(printf) ("%'13llu bytes have already been mmap-ed ANONYMOUS.\n",
                      VG_(am_get_anonsize_total)());
         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: vg_assert (0);
            }
         }

         ret = 1;
         break;
      case  5: /* scheduler */
         VG_(show_sched_status) (True,  // host_stacktrace
                                 True,  // stack_usage
                                 True); // exited_threads
         ret = 1;
         break;
      case  6: /* stats */
         VG_(print_all_stats)(False, /* Memory stats */
                              True   /* Tool stats */);
         ret = 1;
         break;
      case  7: /* open_fds */
         if (VG_(clo_track_fds))
            VG_(show_open_fds) ("");
         else
            VG_(gdb_printf)
               ("Valgrind must be started with --track-fds=yes"
                " to show open fds\n");
         ret = 1;
         break;
      case  8: /* exectxt */
         VG_(print_ExeContext_stats) (True /* with_stacktraces */);
         ret = 1;
         break;
      case  9: { /* location */
         /* Note: we prefer 'v.info location' and not 'v.info address' as
            v.info address is inconsistent with the GDB (native) 
            command 'info address' which gives the address for a symbol.
            GDB equivalent command of 'v.info location' is 'info symbol'. */
         Addr address;
         SizeT dummy_sz = 0x1234;
         if (VG_(strtok_get_address_and_size) (&address, 
                                               &dummy_sz, &ssaveptr)) {
            // If tool provides location information, use that.
            if (VG_(needs).info_location) {
               VG_TDICT_CALL(tool_info_location, address);
            } 
            // If tool does not provide location info, use the common one.
            // Also use the common to compare with tool when debug log is set.
            if (!VG_(needs).info_location || VG_(debugLog_getLevel)() > 0 ) {
               AddrInfo ai;
               ai.tag = Addr_Undescribed;
               VG_(describe_addr) (address, &ai);
               VG_(pp_addrinfo) (address, &ai);
               VG_(clear_addrinfo) (&ai);
            }
         }
         ret = 1;
         break;
      }
      case 10: { /* unwind */
         Addr address;
         SizeT sz = 1;
         if (VG_(strtok_get_address_and_size) (&address, 
                                               &sz, &ssaveptr)) {
            VG_(ppUnwindInfo) (address, address + sz - 1);
         }
         ret = 1;
         break;
      }

      default:
         vg_assert(0);
      }
      break;
   }
   case  3: /* v.wait */
      wcmd = strtok_r (NULL, " ", &ssaveptr);
      if (wcmd != NULL) {
         int_value = strtol (wcmd, NULL, 10);
         VG_(printf) ("gdbserver: continuing in %d ms ...\n", int_value);
         VG_(poll)(NULL, 0, int_value);
      }
      VG_(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;

      if (VG_(strtok_get_address_and_size) (&address, &verbosity, &ssaveptr)) {
         /* 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;
   }

   case  6: /* v.do */
      ret = 1;
      wcmd = strtok_r (NULL, " ", &ssaveptr);
      switch (VG_(keyword_id) ("expensive_sanity_check_general",
                               wcmd, kwd_report_all)) {
         case -2:
         case -1: break;
         case  0: { /* expensive_sanity_check_general */
            // Temporarily bump up sanity level to check e.g. the malloc arenas.
            const Int save_clo_sanity_level = VG_(clo_sanity_level);
            if (VG_(clo_sanity_level) < 4) VG_(clo_sanity_level) = 4;
            VG_(sanity_check_general) (/* force_expensive */ True);
            VG_(clo_sanity_level) = save_clo_sanity_level;
            break;
         }
         default: vg_assert (0);
      }
      break;

   default:
      vg_assert (0);
   }
   return ret;
}