hotspot/agent/src/os/solaris/dbx/svc_agent_dbx.cpp
changeset 10864 17307b96ae38
parent 10779 5037da29fa26
parent 10762 cc1f5ce8e504
child 10865 7f8dd1713604
equal deleted inserted replaced
10779:5037da29fa26 10864:17307b96ae38
     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 }