1 /*
   2  * Copyright (c) 2000, 2002, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 // This is the implementation of a very simple dbx import module which
  26 // handles requests from the VM which come in over a socket. The
  27 // higher-level Java wrapper for dbx starts the debugger, attaches to
  28 // the process, imports this command, and runs it. After that, the SA
  29 // writes commands to this agent via its own private communications
  30 // channel. The intent is to move away from the text-based front-end
  31 // completely in the near future (no more calling "debug" by printing
  32 // text to dbx's stdin).
  33 
  34 #include <stdio.h>
  35 #include <errno.h>
  36 #include <ctype.h>
  37 #include <sys/types.h>
  38 #include <sys/socket.h>
  39 #include <unistd.h>
  40 #include <string.h>
  41 #include <stropts.h>
  42 #include <netinet/in.h>
  43 #include <netinet/tcp.h>
  44 
  45 #include <proc_service.h>
  46 #include <sys/procfs_isa.h>
  47 #include <rtld_db.h>
  48 #include "proc_service_2.h"
  49 #include "svc_agent_dbx.hpp"
  50 
  51 static ServiceabilityAgentDbxModule* module = NULL;
  52 #define NEEDS_CLEANUP
  53 
  54 // Useful for debugging
  55 #define VERBOSE_DEBUGGING
  56 
  57 #ifdef VERBOSE_DEBUGGING
  58 # define debug_only(x) x
  59 #else
  60 # define debug_only(x)
  61 #endif
  62 
  63 // For profiling
  64 //#define PROFILING
  65 
  66 #ifdef PROFILING
  67 #define PROFILE_COUNT 200
  68 static Timer scanTimer;
  69 static Timer workTimer;
  70 static Timer writeTimer;
  71 static int numRequests = 0;
  72 #endif /* PROFILING */
  73 
  74 const char* ServiceabilityAgentDbxModule::CMD_ADDRESS_SIZE   = "address_size";
  75 const char* ServiceabilityAgentDbxModule::CMD_PEEK_FAIL_FAST = "peek_fail_fast";
  76 const char* ServiceabilityAgentDbxModule::CMD_PEEK           = "peek";
  77 const char* ServiceabilityAgentDbxModule::CMD_POKE           = "poke";
  78 const char* ServiceabilityAgentDbxModule::CMD_MAPPED         = "mapped";
  79 const char* ServiceabilityAgentDbxModule::CMD_LOOKUP         = "lookup";
  80 const char* ServiceabilityAgentDbxModule::CMD_THR_GREGS      = "thr_gregs";
  81 const char* ServiceabilityAgentDbxModule::CMD_EXIT           = "exit";
  82 
  83 // The initialization routines must not have C++ name mangling
  84 extern "C" {
  85 
  86 /** This is the initialization routine called by dbx upon importing of
  87     this module. Returns 0 upon successful initialization, -1 upon
  88     failure. */
  89 int shell_imp_init(int major, int minor,
  90                    shell_imp_interp_t interp, int argc, char *argv[])
  91 {
  92   // Ensure shell interpreter data structure is laid out the way we
  93   // expect
  94   if (major != SHELL_IMP_MAJOR) {
  95     debug_only(fprintf(stderr, "Serviceability agent: unexpected value for SHELL_IMP_MAJOR (got %d, expected %d)\n", major, SHELL_IMP_MAJOR);)
  96     return -1;
  97   }
  98   if (minor < SHELL_IMP_MINOR) {
  99     debug_only(fprintf(stderr, "Serviceability agent: unexpected value for SHELL_IMP_MINOR (got %d, expected >= %d)\n", minor, SHELL_IMP_MINOR);)
 100     return -1;
 101   }
 102 
 103   if (module != NULL) {
 104     debug_only(fprintf(stderr, "Serviceability agent: module appears to already be initialized (should not happen)\n");)
 105     // Already initialized. Should not happen.
 106     return -1;
 107   }
 108 
 109   module = new ServiceabilityAgentDbxModule(major, minor, interp, argc, argv);
 110   if (!module->install()) {
 111     debug_only(fprintf(stderr, "Serviceability agent: error installing import module\n");)
 112     delete module;
 113     module = NULL;
 114     return -1;
 115   }
 116 
 117   // Installation was successful. Next step will be for the user to
 118   // enter the appropriate command on the command line, which will
 119   // make the SA's dbx module wait for commands to come in over the
 120   // socket.
 121   return 0;
 122 }
 123 
 124 /** This is the routine called by dbx upon unloading of this module.
 125     Returns 0 upon success, -1 upon failure. */
 126 int
 127 shell_imp_fini(shell_imp_interp_t)
 128 {
 129   if (module == NULL) {
 130     return -1;
 131   }
 132 
 133   bool res = module->uninstall();
 134   delete module;
 135   module = NULL;
 136   if (!res) {
 137     return -1;
 138   }
 139   return 0;
 140 }
 141 
 142 } // extern "C"
 143 
 144 /** This is the routine which is called by the dbx shell when the user
 145     requests the serviceability agent module to run. This delegates to
 146     ServiceabilityAgentDbxModule::run. This routine's signature must
 147     match that of shell_imp_fun_t. */
 148 extern "C" {
 149 static int
 150 svc_agent_run(shell_imp_interp_t, int, char **, void *) {
 151   if (module == NULL) {
 152     return -1;
 153   }
 154 
 155   module->run();
 156   return 0;
 157 }
 158 }
 159 
 160 /*
 161  * Implementation of ServiceabilityAgentDbxModule class
 162  */
 163 
 164 // NOTE: we need to forward declare the special "ps_get_prochandle2"
 165 // function which allows examination of core files as well. It isn't
 166 // currently in proc_service_2.h. Note also that it has name mangling
 167 // because it isn't declared extern "C".
 168 //const struct ps_prochandle *ps_get_prochandle2(int cores_too);
 169 
 170 ServiceabilityAgentDbxModule::ServiceabilityAgentDbxModule(int, int, shell_imp_interp_t interp,
 171                                                            int argc, char *argv[])
 172   :myComm(32768, 131072)
 173 {
 174   _interp = interp;
 175   _argc = argc;
 176   _argv = argv;
 177   _tdb_agent = NULL;
 178   peek_fail_fast = false;
 179   libThreadName = NULL;
 180 }
 181 
 182 ServiceabilityAgentDbxModule::~ServiceabilityAgentDbxModule() {
 183   if (_command != NULL) {
 184     uninstall();
 185   }
 186 }
 187 
 188 char*
 189 readCStringFromProcess(psaddr_t addr) {
 190   char c;
 191   int num = 0;
 192   ps_prochandle* cur_proc = (ps_prochandle*) ps_get_prochandle2(1);
 193 
 194   // Search for null terminator
 195   do {
 196     if (ps_pread(cur_proc, addr + num, &c, 1) != PS_OK) {
 197       return NULL;
 198     }
 199     ++num;
 200   } while (c != 0);
 201 
 202   // Allocate string
 203   char* res = new char[num];
 204   if (ps_pread(cur_proc, addr, res, num) != PS_OK) {
 205     delete[] res;
 206     return NULL;
 207   }
 208   return res;
 209 }
 210 
 211 int
 212 findLibThreadCB(const rd_loadobj_t* lo, void* data) {
 213   ServiceabilityAgentDbxModule* module = (ServiceabilityAgentDbxModule*) data;
 214   char* name = readCStringFromProcess(lo->rl_nameaddr);
 215   if (strstr(name, "libthread.so") != NULL) {
 216     module->libThreadName = name;
 217     return 0;
 218   } else {
 219     delete[] name;
 220     return 1;
 221   }
 222 }
 223 
 224 bool
 225 ServiceabilityAgentDbxModule::install() {
 226   // NOTE interdependency between here and Java side wrapper
 227   // FIXME: casts of string literal to char * to match prototype
 228   _command = shell_imp_define_command((char *) "svc_agent_run",
 229                                       &svc_agent_run,
 230                                       0,
 231                                       NULL,
 232                                       (char *) "Run the serviceability agent's dbx module.\n"
 233                                       "This routine causes the module to listen on a socket for requests.\n"
 234                                       "It does not return until the Java-side code tells it to exit, at\n"
 235                                       "which point control is returned to the dbx shell.");
 236   if (_command == NULL) {
 237     debug_only(fprintf(stderr, "Serviceability agent: Failed to install svc_agent_run command\n"));
 238     return false;
 239   }
 240 
 241   // This is fairly painful. Since dbx doesn't currently load
 242   // libthread_db with RTLD_GLOBAL, we can't just use RTLD_DEFAULT for
 243   // the argument to dlsym. Instead, we have to use rtld_db to search
 244   // through the loaded objects in the target process for libthread.so and
 245 
 246   // Try rtld_db
 247   if (rd_init(RD_VERSION) != RD_OK) {
 248     debug_only(fprintf(stderr, "Serviceability agent: Unable to init rtld_db\n"));
 249     return false;
 250   }
 251 
 252   rd_agent_t* rda = rd_new((struct ps_prochandle*) ps_get_prochandle2(1));
 253   if (rda == NULL) {
 254     debug_only(fprintf(stderr, "Serviceability agent: Unable to allocate rtld_db agent\n"));
 255     return false;
 256   }
 257 
 258   if (rd_loadobj_iter(rda, (rl_iter_f*) findLibThreadCB, this) != RD_OK) {
 259     debug_only(fprintf(stderr, "Serviceability agent: Loadobject iteration failed\n"));
 260     return false;
 261   }
 262 
 263   if (libThreadName == NULL) {
 264     debug_only(fprintf(stderr, "Serviceability agent: Failed to find pathname to libthread.so in target process\n"));
 265     return false;
 266   }
 267 
 268   // Find and open libthread_db.so
 269   char* slash = strrchr(libThreadName, '/');
 270   if (slash == NULL) {
 271     debug_only(fprintf(stderr, "Serviceability agent: can't parse path to libthread.so \"%s\"\n"));
 272     return false;
 273   }
 274 
 275   int slashPos = slash - libThreadName;
 276   char* buf = new char[slashPos + strlen("libthread_db.so") + 20]; // slop
 277   if (buf == NULL) {
 278     debug_only(fprintf(stderr, "Serviceability agent: error allocating libthread_db.so pathname\n"));
 279     return false;
 280   }
 281   strncpy(buf, libThreadName, slashPos + 1);
 282 
 283   // Check dbx's data model; use sparcv9/ subdirectory if 64-bit and
 284   // if target process is 32-bit
 285   if ((sizeof(void*) == 8) &&
 286       (strstr(libThreadName, "sparcv9") == NULL)) {
 287     strcpy(buf + slashPos + 1, "sparcv9/");
 288     slashPos += strlen("sparcv9/");
 289   }
 290 
 291   strcpy(buf + slashPos + 1, "libthread_db.so");
 292 
 293   libThreadDB = dlopen(buf, RTLD_LAZY);
 294   void* tmpDB = libThreadDB;
 295   if (libThreadDB == NULL) {
 296     debug_only(fprintf(stderr, "Serviceability agent: Warning: unable to find libthread_db.so at \"%s\"\n", buf));
 297     // Would like to handle this case as well. Maybe dbx has a better
 298     // idea of where libthread_db.so lies. If the problem with dbx
 299     // loading libthread_db without RTLD_GLOBAL specified ever gets
 300     // fixed, we could run this code all the time.
 301     tmpDB = RTLD_DEFAULT;
 302   }
 303 
 304   delete[] buf;
 305 
 306   // Initialize access to libthread_db
 307   td_init_fn          = (td_init_fn_t*)          dlsym(tmpDB, "td_init");
 308   td_ta_new_fn        = (td_ta_new_fn_t*)        dlsym(tmpDB, "td_ta_new");
 309   td_ta_delete_fn     = (td_ta_delete_fn_t*)     dlsym(tmpDB, "td_ta_delete");
 310   td_ta_map_id2thr_fn = (td_ta_map_id2thr_fn_t*) dlsym(tmpDB, "td_ta_map_id2thr");
 311   td_thr_getgregs_fn  = (td_thr_getgregs_fn_t*)  dlsym(tmpDB, "td_thr_getgregs");
 312 
 313   if (td_init_fn == NULL ||
 314       td_ta_new_fn == NULL ||
 315       td_ta_delete_fn == NULL ||
 316       td_ta_map_id2thr_fn == NULL ||
 317       td_thr_getgregs_fn == NULL) {
 318     debug_only(fprintf(stderr, "Serviceability agent: Failed to find one or more libthread_db symbols:\n"));
 319     debug_only(if (td_init_fn == NULL)          fprintf(stderr, "  td_init\n"));
 320     debug_only(if (td_ta_new_fn == NULL)        fprintf(stderr, "  td_ta_new\n"));
 321     debug_only(if (td_ta_delete_fn == NULL)     fprintf(stderr, "  td_ta_delete\n"));
 322     debug_only(if (td_ta_map_id2thr_fn == NULL) fprintf(stderr, "  td_ta_map_id2thr\n"));
 323     debug_only(if (td_thr_getgregs_fn == NULL)  fprintf(stderr, "  td_thr_getgregs\n"));
 324     return false;
 325   }
 326 
 327   if ((*td_init_fn)() != TD_OK) {
 328     debug_only(fprintf(stderr, "Serviceability agent: Failed to initialize libthread_db\n"));
 329     return false;
 330   }
 331 
 332   return true;
 333 }
 334 
 335 bool
 336 ServiceabilityAgentDbxModule::uninstall() {
 337   if (_command == NULL) {
 338     return false;
 339   }
 340 
 341   if (libThreadDB != NULL) {
 342     dlclose(libThreadDB);
 343     libThreadDB = NULL;
 344   }
 345 
 346   int res = shell_imp_undefine_command(_command);
 347 
 348   if (res != 0) {
 349     return false;
 350   }
 351 
 352   return true;
 353 }
 354 
 355 bool
 356 ServiceabilityAgentDbxModule::run() {
 357   // This is where most of the work gets done.
 358   // The command processor loop looks like the following:
 359   //  - create listening socket
 360   //  - accept a connection (only one for now)
 361   //  - while that connection is open and the "exit" command has not
 362   //    been received:
 363   //    - read command
 364   //    - if it's the exit command, cleanup and return
 365   //    - otherwise, process command and write result
 366 
 367   int listening_socket = socket(AF_INET, SOCK_STREAM, 0);
 368   if (listening_socket < 0) {
 369     return false;
 370   }
 371 
 372   // Set the SO_REUSEADDR property on the listening socket. This
 373   // prevents problems with calls to bind() to the same port failing
 374   // after this process exits. This seems to work on all platforms.
 375   int reuse_address = 1;
 376   if (setsockopt(listening_socket, SOL_SOCKET, SO_REUSEADDR,
 377                  (char *)&reuse_address, sizeof(reuse_address)) < 0) {
 378     close(listening_socket);
 379     return false;
 380   }
 381 
 382   sockaddr_in server_address;
 383   // Build the server address. We can bind the listening socket to the
 384   // INADDR_ANY internet address.
 385   memset((char*)&server_address, 0, sizeof(server_address));
 386   server_address.sin_family = AF_INET;
 387   server_address.sin_addr.s_addr = (unsigned long)htonl(INADDR_ANY);
 388   server_address.sin_port = htons((short)PORT);
 389 
 390   // Bind socket to port
 391   if (bind(listening_socket, (sockaddr*) &server_address,
 392            sizeof(server_address)) < 0) {
 393     close(listening_socket);
 394     return false;
 395   }
 396 
 397   // Arbitrarily chosen backlog of 5 (shouldn't matter since we expect
 398   // at most one connection)
 399   if (listen(listening_socket, 5) < 0) {
 400     close(listening_socket);
 401     return false;
 402   }
 403 
 404   // OK, now ready to wait for a data connection. This call to
 405   // accept() will block.
 406   struct sockaddr_in client_address;
 407   int address_len   = sizeof(client_address);
 408   int client_socket = accept(listening_socket, (sockaddr*) &client_address,
 409                          &address_len);
 410   // Close listening socket regardless of whether accept() succeeded.
 411   // (FIXME: this may be annoying, especially during debugging, but I
 412   // really feel that robustness and multiple connections should be
 413   // handled higher up, e.g., at the Java level -- multiple clients
 414   // could conceivably connect to the SA via RMI, and that would be a
 415   // more robust solution than implementing multiple connections at
 416   // this level)
 417   NEEDS_CLEANUP;
 418 
 419   // NOTE: the call to shutdown() usually fails, so don't panic if this happens
 420   shutdown(listening_socket, 2);
 421 
 422   if (close(listening_socket) < 0) {
 423     debug_only(fprintf(stderr, "Serviceability agent: Error closing listening socket\n"));
 424     return false;
 425   }
 426 
 427   if (client_socket < 0) {
 428     debug_only(fprintf(stderr, "Serviceability agent: Failed to open client socket\n"));
 429     // No more cleanup necessary
 430     return false;
 431   }
 432 
 433   // Attempt to disable TCP buffering on this socket. We send small
 434   // amounts of data back and forth and don't want buffering.
 435   int buffer_val = 1;
 436   if (setsockopt(client_socket, IPPROTO_IP, TCP_NODELAY, (char *) &buffer_val, sizeof(buffer_val)) < 0) {
 437     debug_only(fprintf(stderr, "Serviceability agent: Failed to set TCP_NODELAY option on client socket\n"));
 438     cleanup(client_socket);
 439     return false;
 440   }
 441 
 442   // OK, we have the data socket through which we will communicate
 443   // with the Java side. Wait for commands or until reading or writing
 444   // caused an error.
 445 
 446   bool should_continue = true;
 447 
 448   myComm.setSocket(client_socket);
 449 
 450 #ifdef PROFILING
 451   scanTimer.reset();
 452   workTimer.reset();
 453   writeTimer.reset();
 454 #endif
 455 
 456   // Allocate a new thread agent for libthread_db
 457   if ((*td_ta_new_fn)((ps_prochandle*) ps_get_prochandle2(1), &_tdb_agent) !=
 458       TD_OK) {
 459     debug_only(fprintf(stderr, "Serviceability agent: Failed to allocate thread agent\n"));
 460     cleanup(client_socket);
 461     return false;
 462   }
 463 
 464   do {
 465     // Decided to use text to communicate between these processes.
 466     // Probably will make debugging easier -- could telnet in if
 467     // necessary. Will make scanning harder, but probably doesn't
 468     // matter.
 469 
 470     // Why not just do what workshop does and parse dbx's console?
 471     // Probably could do that, but at least this way we are in control
 472     // of the text format on both ends.
 473 
 474     // FIXME: should have some way of synchronizing these commands
 475     // between the C and Java sources.
 476 
 477     NEEDS_CLEANUP;
 478 
 479     // Do a blocking read of a line from the socket.
 480     char *input_buffer = myComm.readLine();
 481     if (input_buffer == NULL) {
 482       debug_only(fprintf(stderr, "Serviceability agent: error during read: errno = %d\n", errno));
 483       debug_only(perror("Serviceability agent"));
 484       // Error occurred during read.
 485       // FIXME: should guard against SIGPIPE
 486       cleanup(client_socket);
 487       return false;
 488     }
 489 
 490     // OK, now ready to scan. See README-commands.txt for syntax
 491     // descriptions.
 492 
 493     bool res = false;
 494     if (!strncmp(input_buffer, CMD_ADDRESS_SIZE, strlen(CMD_ADDRESS_SIZE))) {
 495       res = handleAddressSize(input_buffer + strlen(CMD_ADDRESS_SIZE));
 496     } else if (!strncmp(input_buffer, CMD_PEEK_FAIL_FAST, strlen(CMD_PEEK_FAIL_FAST))) {
 497       res = handlePeekFailFast(input_buffer + strlen(CMD_PEEK_FAIL_FAST));
 498     } else if (!strncmp(input_buffer, CMD_PEEK, strlen(CMD_PEEK))) {
 499       res = handlePeek(input_buffer + strlen(CMD_PEEK));
 500     } else if (!strncmp(input_buffer, CMD_POKE, strlen(CMD_POKE))) {
 501       res = handlePoke(input_buffer + strlen(CMD_POKE));
 502     } else if (!strncmp(input_buffer, CMD_MAPPED, strlen(CMD_MAPPED))) {
 503       res = handleMapped(input_buffer + strlen(CMD_MAPPED));
 504     } else if (!strncmp(input_buffer, CMD_LOOKUP, strlen(CMD_LOOKUP))) {
 505       res = handleLookup(input_buffer + strlen(CMD_LOOKUP));
 506     } else if (!strncmp(input_buffer, CMD_THR_GREGS, strlen(CMD_THR_GREGS))) {
 507       res = handleThrGRegs(input_buffer + strlen(CMD_THR_GREGS));
 508     } else if (!strncmp(input_buffer, CMD_EXIT, strlen(CMD_EXIT))) {
 509       should_continue = false;
 510     }
 511 
 512     if (should_continue) {
 513       if (!res) {
 514         cleanup(client_socket);
 515         return false;
 516       }
 517     }
 518 
 519 #ifdef PROFILING
 520     if (++numRequests == PROFILE_COUNT) {
 521       fprintf(stderr, "%d requests: %d ms scanning, %d ms work, %d ms writing\n",
 522               PROFILE_COUNT, scanTimer.total(), workTimer.total(), writeTimer.total());
 523       fflush(stderr);
 524       scanTimer.reset();
 525       workTimer.reset();
 526       writeTimer.reset();
 527       numRequests = 0;
 528     }
 529 #endif
 530 
 531   } while (should_continue);
 532 
 533   // Successful exit
 534   cleanup(client_socket);
 535   return true;
 536 }
 537 
 538 void
 539 ServiceabilityAgentDbxModule::cleanup(int client_socket) {
 540   shutdown(client_socket, 2);
 541   close(client_socket);
 542   if (_tdb_agent != NULL) {
 543     (*td_ta_delete_fn)(_tdb_agent);
 544   }
 545 }
 546 
 547 bool
 548 ServiceabilityAgentDbxModule::handleAddressSize(char* data) {
 549   int data_model;
 550   ps_err_e result = ps_pdmodel((ps_prochandle*) ps_get_prochandle2(1),
 551                                &data_model);
 552   if (result != PS_OK) {
 553     myComm.writeString("0");
 554     myComm.flush();
 555     return false;
 556   }
 557 
 558   int val;
 559   switch (data_model) {
 560   case PR_MODEL_ILP32:
 561     val = 32;
 562     break;
 563   case PR_MODEL_LP64:
 564     val = 64;
 565     break;
 566   default:
 567     val = 0;
 568     break;
 569   }
 570 
 571   if (!myComm.writeInt(val)) {
 572     return false;
 573   }
 574   if (!myComm.writeEOL()) {
 575     return false;
 576   }
 577   return myComm.flush();
 578 }
 579 
 580 bool
 581 ServiceabilityAgentDbxModule::handlePeekFailFast(char* data) {
 582   unsigned int val;
 583   if (!scanUnsignedInt(&data, &val)) {
 584     return false;
 585   }
 586   peek_fail_fast = (val ? true : false);
 587   return true;
 588 }
 589 
 590 bool
 591 ServiceabilityAgentDbxModule::handlePeek(char* data) {
 592   // Scan hex address, return false if failed
 593   psaddr_t addr;
 594 #ifdef PROFILING
 595   scanTimer.start();
 596 #endif /* PROFILING */
 597   if (!scanAddress(&data, &addr)) {
 598     return false;
 599   }
 600   unsigned int num;
 601   if (!scanUnsignedInt(&data, &num)) {
 602     return false;
 603   }
 604   if (num == 0) {
 605 #ifdef PROFILING
 606     writeTimer.start();
 607 #endif /* PROFILING */
 608     myComm.writeBinChar('B');
 609     myComm.writeBinChar(1);
 610     myComm.writeBinUnsignedInt(0);
 611     myComm.writeBinChar(0);
 612 #ifdef PROFILING
 613     writeTimer.stop();
 614 #endif /* PROFILING */
 615     return true;
 616   }
 617 #ifdef PROFILING
 618   scanTimer.stop();
 619   workTimer.start();
 620 #endif /* PROFILING */
 621   char* buf = new char[num];
 622   ps_prochandle* cur_proc = (ps_prochandle*) ps_get_prochandle2(1);
 623   ps_err_e result = ps_pread(cur_proc, addr, buf, num);
 624   if (result == PS_OK) {
 625     // Fast case; entire read succeeded.
 626 #ifdef PROFILING
 627     workTimer.stop();
 628     writeTimer.start();
 629 #endif /* PROFILING */
 630     myComm.writeBinChar('B');
 631     myComm.writeBinChar(1);
 632     myComm.writeBinUnsignedInt(num);
 633     myComm.writeBinChar(1);
 634     myComm.writeBinBuf(buf, num);
 635 #ifdef PROFILING
 636     writeTimer.stop();
 637 #endif /* PROFILING */
 638   } else {
 639 #ifdef PROFILING
 640     workTimer.stop();
 641 #endif /* PROFILING */
 642 
 643     if (peek_fail_fast) {
 644 #ifdef PROFILING
 645     writeTimer.start();
 646 #endif /* PROFILING */
 647       // Fail fast
 648       myComm.writeBinChar('B');
 649       myComm.writeBinChar(1);
 650       myComm.writeBinUnsignedInt(num);
 651       myComm.writeBinChar(0);
 652 #ifdef PROFILING
 653     writeTimer.stop();
 654 #endif /* PROFILING */
 655     } else {
 656       // Slow case: try to read one byte at a time
 657       // FIXME: need better way of handling this, a la VirtualQuery
 658 
 659       unsigned int  strideLen      = 0;
 660       int           bufIdx         = 0;
 661       bool          lastByteMapped = (ps_pread(cur_proc, addr, buf, 1) == PS_OK ? true : false);
 662 
 663 #ifdef PROFILING
 664       writeTimer.start();
 665 #endif /* PROFILING */
 666       myComm.writeBinChar('B');
 667       myComm.writeBinChar(1);
 668 #ifdef PROFILING
 669       writeTimer.stop();
 670 #endif /* PROFILING */
 671 
 672       for (int i = 0; i < num; ++i, ++addr) {
 673 #ifdef PROFILING
 674         workTimer.start();
 675 #endif /* PROFILING */
 676         result = ps_pread(cur_proc, addr, &buf[bufIdx], 1);
 677 #ifdef PROFILING
 678         workTimer.stop();
 679 #endif /* PROFILING */
 680         bool tmpMapped = (result == PS_OK ? true : false);
 681 #ifdef PROFILING
 682         writeTimer.start();
 683 #endif /* PROFILING */
 684         if (tmpMapped != lastByteMapped) {
 685           // State change. Write the length of the last stride.
 686           myComm.writeBinUnsignedInt(strideLen);
 687           if (lastByteMapped) {
 688             // Stop gathering data. Write the data of the last stride.
 689             myComm.writeBinChar(1);
 690             myComm.writeBinBuf(buf, strideLen);
 691             bufIdx = 0;
 692           } else {
 693             // Start gathering data to write.
 694             myComm.writeBinChar(0);
 695           }
 696           strideLen = 0;
 697           lastByteMapped = tmpMapped;
 698         }
 699 #ifdef PROFILING
 700         writeTimer.stop();
 701 #endif /* PROFILING */
 702         if (lastByteMapped) {
 703           ++bufIdx;
 704         }
 705         ++strideLen;
 706       }
 707 
 708       // Write last stride (must be at least one byte long by definition)
 709 #ifdef PROFILING
 710       writeTimer.start();
 711 #endif /* PROFILING */
 712       myComm.writeBinUnsignedInt(strideLen);
 713       if (lastByteMapped) {
 714         myComm.writeBinChar(1);
 715         myComm.writeBinBuf(buf, strideLen);
 716       } else {
 717         myComm.writeBinChar(0);
 718       }
 719 #ifdef PROFILING
 720       writeTimer.stop();
 721 #endif /* PROFILING */
 722     }
 723   }
 724   delete[] buf;
 725   myComm.flush();
 726   return true;
 727 }
 728 
 729 bool
 730 ServiceabilityAgentDbxModule::handlePoke(char* data) {
 731   // FIXME: not yet implemented
 732   NEEDS_CLEANUP;
 733   bool res = myComm.writeBoolAsInt(false);
 734   myComm.flush();
 735   return res;
 736 }
 737 
 738 bool
 739 ServiceabilityAgentDbxModule::handleMapped(char* data) {
 740   // Scan address
 741   psaddr_t addr;
 742   if (!scanAddress(&data, &addr)) {
 743     return false;
 744   }
 745   unsigned int num;
 746   if (!scanUnsignedInt(&data, &num)) {
 747     return false;
 748   }
 749   unsigned char val;
 750   ps_prochandle* cur_proc = (ps_prochandle*) ps_get_prochandle2(1);
 751   char* buf = new char[num];
 752   if (ps_pread(cur_proc, addr, buf, num) == PS_OK) {
 753     myComm.writeBoolAsInt(true);
 754   } else {
 755     myComm.writeBoolAsInt(false);
 756   }
 757   delete[] buf;
 758   myComm.writeEOL();
 759   myComm.flush();
 760   return true;
 761 }
 762 
 763 extern "C"
 764 int loadobj_iterator(const rd_loadobj_t* loadobj, void *) {
 765   if (loadobj != NULL) {
 766     fprintf(stderr, "loadobj_iterator: visited loadobj \"%p\"\n", (void*) loadobj->rl_nameaddr);
 767     return 1;
 768   }
 769 
 770   fprintf(stderr, "loadobj_iterator: NULL loadobj\n");
 771   return 0;
 772 }
 773 
 774 bool
 775 ServiceabilityAgentDbxModule::handleLookup(char* data) {
 776   // Debugging: iterate over loadobjs
 777   /*
 778   rd_agent_t* rld_agent = rd_new((ps_prochandle*) ps_get_prochandle2(1));
 779   rd_loadobj_iter(rld_agent, &loadobj_iterator, NULL);
 780   rd_delete(rld_agent);
 781   */
 782 
 783 #ifdef PROFILING
 784   scanTimer.start();
 785 #endif /* PROFILING */
 786 
 787   char* object_name = scanSymbol(&data);
 788   if (object_name == NULL) {
 789     return false;
 790   }
 791   char* symbol_name = scanSymbol(&data);
 792   if (symbol_name == NULL) {
 793     delete[] object_name;
 794     return false;
 795   }
 796 
 797 #ifdef PROFILING
 798   scanTimer.stop();
 799   workTimer.start();
 800 #endif /* PROFILING */
 801 
 802   ps_sym_t sym;
 803   // FIXME: check return values from write routines
 804   ps_prochandle* process = (ps_prochandle*) ps_get_prochandle2(1);
 805   ps_err_e lookup_res = ps_pglobal_sym(process,
 806                                        object_name, symbol_name, &sym);
 807 #ifdef PROFILING
 808   workTimer.stop();
 809   writeTimer.start();
 810 #endif /* PROFILING */
 811 
 812   delete[] object_name;
 813   delete[] symbol_name;
 814   if (lookup_res != PS_OK) {
 815     // This is too noisy
 816     //    debug_only(fprintf(stderr, "ServiceabilityAgentDbxModule::handleLookup: error %d\n", lookup_res));
 817     myComm.writeString("0x0");
 818   } else {
 819     myComm.writeAddress((void *)sym.st_value);
 820   }
 821   myComm.writeEOL();
 822   myComm.flush();
 823 
 824 #ifdef PROFILING
 825   writeTimer.stop();
 826 #endif /* PROFILING */
 827 
 828   return true;
 829 }
 830 
 831 bool
 832 ServiceabilityAgentDbxModule::handleThrGRegs(char* data) {
 833 #ifdef PROFILING
 834   scanTimer.start();
 835 #endif /* PROFILING */
 836 
 837   unsigned int num;
 838   // Get the thread ID
 839   if (!scanUnsignedInt(&data, &num)) {
 840     return false;
 841   }
 842 
 843 #ifdef PROFILING
 844   scanTimer.stop();
 845   workTimer.start();
 846 #endif /* PROFILING */
 847 
 848   // Map tid to thread handle
 849   td_thrhandle_t thread_handle;
 850   if ((*td_ta_map_id2thr_fn)(_tdb_agent, num, &thread_handle) != TD_OK) {
 851     //    fprintf(stderr, "Error mapping thread ID %d to thread handle\n", num);
 852     return false;
 853   }
 854 
 855   // Fetch register set
 856   prgregset_t reg_set;
 857   memset(reg_set, 0, sizeof(reg_set));
 858   td_err_e result = (*td_thr_getgregs_fn)(&thread_handle, reg_set);
 859   if ((result != TD_OK) && (result != TD_PARTIALREG)) {
 860     //    fprintf(stderr, "Error fetching registers for thread handle %d: error = %d\n", num, result);
 861     return false;
 862   }
 863 
 864 #ifdef PROFILING
 865   workTimer.stop();
 866   writeTimer.start();
 867 #endif /* PROFILING */
 868 
 869 #if (defined(__sparc) || defined(__i386))
 870   myComm.writeInt(NPRGREG);
 871   myComm.writeSpace();
 872   for (int i = 0; i < NPRGREG; i++) {
 873     myComm.writeAddress((void *)reg_set[i]);
 874     if (i == NPRGREG - 1) {
 875       myComm.writeEOL();
 876     } else {
 877       myComm.writeSpace();
 878     }
 879   }
 880 #else
 881 #error  Please port ServiceabilityAgentDbxModule::handleThrGRegs to your current platform
 882 #endif
 883 
 884   myComm.flush();
 885 
 886 #ifdef PROFILING
 887   writeTimer.stop();
 888 #endif /* PROFILING */
 889 
 890   return true;
 891 }
 892 
 893 //
 894 // Input routines
 895 //
 896 
 897 bool
 898 ServiceabilityAgentDbxModule::scanAddress(char** data, psaddr_t* addr) {
 899   *addr = 0;
 900 
 901   // Skip whitespace
 902   while ((**data != 0) && (isspace(**data))) {
 903     ++*data;
 904   }
 905 
 906   if (**data == 0) {
 907     return false;
 908   }
 909 
 910   if (strncmp(*data, "0x", 2) != 0) {
 911     return false;
 912   }
 913 
 914   *data += 2;
 915 
 916   while ((**data != 0) && (!isspace(**data))) {
 917     int val;
 918     bool res = charToNibble(**data, &val);
 919     if (!res) {
 920       return false;
 921     }
 922     *addr <<= 4;
 923     *addr |= val;
 924     ++*data;
 925   }
 926 
 927   return true;
 928 }
 929 
 930 bool
 931 ServiceabilityAgentDbxModule::scanUnsignedInt(char** data, unsigned int* num) {
 932   *num = 0;
 933 
 934   // Skip whitespace
 935   while ((**data != 0) && (isspace(**data))) {
 936     ++*data;
 937   }
 938 
 939   if (**data == 0) {
 940     return false;
 941   }
 942 
 943   while ((**data != 0) && (!isspace(**data))) {
 944     char cur = **data;
 945     if ((cur < '0') || (cur > '9')) {
 946       return false;
 947     }
 948     *num *= 10;
 949     *num += cur - '0';
 950     ++*data;
 951   }
 952 
 953   return true;
 954 }
 955 
 956 char*
 957 ServiceabilityAgentDbxModule::scanSymbol(char** data) {
 958   // Skip whitespace
 959   while ((**data != 0) && (isspace(**data))) {
 960     ++*data;
 961   }
 962 
 963   if (**data == 0) {
 964     return NULL;
 965   }
 966 
 967   // First count length
 968   int len = 1; // Null terminator
 969   char* tmpData = *data;
 970   while ((*tmpData != 0) && (!isspace(*tmpData))) {
 971     ++tmpData;
 972     ++len;
 973   }
 974   char* buf = new char[len];
 975   strncpy(buf, *data, len - 1);
 976   buf[len - 1] = 0;
 977   *data += len - 1;
 978   return buf;
 979 }
 980 
 981 bool
 982 ServiceabilityAgentDbxModule::charToNibble(char ascii, int* value) {
 983   if (ascii >= '0' && ascii <= '9') {
 984     *value = ascii - '0';
 985     return true;
 986   } else if (ascii >= 'A' && ascii <= 'F') {
 987     *value = 10 + ascii - 'A';
 988     return true;
 989   } else if (ascii >= 'a' && ascii <= 'f') {
 990     *value = 10 + ascii - 'a';
 991     return true;
 992   }
 993 
 994   return false;
 995 }
 996 
 997 
 998 char*
 999 ServiceabilityAgentDbxModule::readCStringFromProcess(psaddr_t addr) {
1000   char c;
1001   int num = 0;
1002   ps_prochandle* cur_proc = (ps_prochandle*) ps_get_prochandle2(1);
1003 
1004   // Search for null terminator
1005   do {
1006     if (ps_pread(cur_proc, addr + num, &c, 1) != PS_OK) {
1007       return NULL;
1008     }
1009     ++num;
1010   } while (c != 0);
1011 
1012   // Allocate string
1013   char* res = new char[num];
1014   if (ps_pread(cur_proc, addr, res, num) != PS_OK) {
1015     delete[] res;
1016     return NULL;
1017   }
1018   return res;
1019 }
1020 
1021 
1022 //--------------------------------------------------------------------------------
1023 // Class Timer
1024 //
1025 
1026 Timer::Timer() {
1027   reset();
1028 }
1029 
1030 Timer::~Timer() {
1031 }
1032 
1033 void
1034 Timer::start() {
1035   gettimeofday(&startTime, NULL);
1036 }
1037 
1038 void
1039 Timer::stop() {
1040   struct timeval endTime;
1041   gettimeofday(&endTime, NULL);
1042   totalMicroseconds += timevalDiff(&startTime, &endTime);
1043   ++counter;
1044 }
1045 
1046 long
1047 Timer::total() {
1048   return (totalMicroseconds / 1000);
1049 }
1050 
1051 long
1052 Timer::average() {
1053   return (long) ((double) total() / (double) counter);
1054 }
1055 
1056 void
1057 Timer::reset() {
1058   totalMicroseconds = 0;
1059   counter = 0;
1060 }
1061 
1062 long long
1063 Timer::timevalDiff(struct timeval* start, struct timeval* end) {
1064   long long secs = end->tv_sec - start->tv_sec;
1065   secs *= 1000000;
1066   long long usecs = end->tv_usec - start->tv_usec;
1067   return (secs + usecs);
1068 }