--- old/agent/src/os/solaris/dbx/svc_agent_dbx.cpp Thu Sep 17 16:09:39 2009 +++ /dev/null Thu Sep 17 16:09:39 2009 @@ -1,1068 +0,0 @@ -/* - * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - * - */ - -// This is the implementation of a very simple dbx import module which -// handles requests from the VM which come in over a socket. The -// higher-level Java wrapper for dbx starts the debugger, attaches to -// the process, imports this command, and runs it. After that, the SA -// writes commands to this agent via its own private communications -// channel. The intent is to move away from the text-based front-end -// completely in the near future (no more calling "debug" by printing -// text to dbx's stdin). - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include "proc_service_2.h" -#include "svc_agent_dbx.hpp" - -static ServiceabilityAgentDbxModule* module = NULL; -#define NEEDS_CLEANUP - -// Useful for debugging -#define VERBOSE_DEBUGGING - -#ifdef VERBOSE_DEBUGGING -# define debug_only(x) x -#else -# define debug_only(x) -#endif - -// For profiling -//#define PROFILING - -#ifdef PROFILING -#define PROFILE_COUNT 200 -static Timer scanTimer; -static Timer workTimer; -static Timer writeTimer; -static int numRequests = 0; -#endif /* PROFILING */ - -const char* ServiceabilityAgentDbxModule::CMD_ADDRESS_SIZE = "address_size"; -const char* ServiceabilityAgentDbxModule::CMD_PEEK_FAIL_FAST = "peek_fail_fast"; -const char* ServiceabilityAgentDbxModule::CMD_PEEK = "peek"; -const char* ServiceabilityAgentDbxModule::CMD_POKE = "poke"; -const char* ServiceabilityAgentDbxModule::CMD_MAPPED = "mapped"; -const char* ServiceabilityAgentDbxModule::CMD_LOOKUP = "lookup"; -const char* ServiceabilityAgentDbxModule::CMD_THR_GREGS = "thr_gregs"; -const char* ServiceabilityAgentDbxModule::CMD_EXIT = "exit"; - -// The initialization routines must not have C++ name mangling -extern "C" { - -/** This is the initialization routine called by dbx upon importing of - this module. Returns 0 upon successful initialization, -1 upon - failure. */ -int shell_imp_init(int major, int minor, - shell_imp_interp_t interp, int argc, char *argv[]) -{ - // Ensure shell interpreter data structure is laid out the way we - // expect - if (major != SHELL_IMP_MAJOR) { - debug_only(fprintf(stderr, "Serviceability agent: unexpected value for SHELL_IMP_MAJOR (got %d, expected %d)\n", major, SHELL_IMP_MAJOR);) - return -1; - } - if (minor < SHELL_IMP_MINOR) { - debug_only(fprintf(stderr, "Serviceability agent: unexpected value for SHELL_IMP_MINOR (got %d, expected >= %d)\n", minor, SHELL_IMP_MINOR);) - return -1; - } - - if (module != NULL) { - debug_only(fprintf(stderr, "Serviceability agent: module appears to already be initialized (should not happen)\n");) - // Already initialized. Should not happen. - return -1; - } - - module = new ServiceabilityAgentDbxModule(major, minor, interp, argc, argv); - if (!module->install()) { - debug_only(fprintf(stderr, "Serviceability agent: error installing import module\n");) - delete module; - module = NULL; - return -1; - } - - // Installation was successful. Next step will be for the user to - // enter the appropriate command on the command line, which will - // make the SA's dbx module wait for commands to come in over the - // socket. - return 0; -} - -/** This is the routine called by dbx upon unloading of this module. - Returns 0 upon success, -1 upon failure. */ -int -shell_imp_fini(shell_imp_interp_t) -{ - if (module == NULL) { - return -1; - } - - bool res = module->uninstall(); - delete module; - module = NULL; - if (!res) { - return -1; - } - return 0; -} - -} // extern "C" - -/** This is the routine which is called by the dbx shell when the user - requests the serviceability agent module to run. This delegates to - ServiceabilityAgentDbxModule::run. This routine's signature must - match that of shell_imp_fun_t. */ -extern "C" { -static int -svc_agent_run(shell_imp_interp_t, int, char **, void *) { - if (module == NULL) { - return -1; - } - - module->run(); - return 0; -} -} - -/* - * Implementation of ServiceabilityAgentDbxModule class - */ - -// NOTE: we need to forward declare the special "ps_get_prochandle2" -// function which allows examination of core files as well. It isn't -// currently in proc_service_2.h. Note also that it has name mangling -// because it isn't declared extern "C". -//const struct ps_prochandle *ps_get_prochandle2(int cores_too); - -ServiceabilityAgentDbxModule::ServiceabilityAgentDbxModule(int, int, shell_imp_interp_t interp, - int argc, char *argv[]) - :myComm(32768, 131072) -{ - _interp = interp; - _argc = argc; - _argv = argv; - _tdb_agent = NULL; - peek_fail_fast = false; - libThreadName = NULL; -} - -ServiceabilityAgentDbxModule::~ServiceabilityAgentDbxModule() { - if (_command != NULL) { - uninstall(); - } -} - -char* -readCStringFromProcess(psaddr_t addr) { - char c; - int num = 0; - ps_prochandle* cur_proc = (ps_prochandle*) ps_get_prochandle2(1); - - // Search for null terminator - do { - if (ps_pread(cur_proc, addr + num, &c, 1) != PS_OK) { - return NULL; - } - ++num; - } while (c != 0); - - // Allocate string - char* res = new char[num]; - if (ps_pread(cur_proc, addr, res, num) != PS_OK) { - delete[] res; - return NULL; - } - return res; -} - -int -findLibThreadCB(const rd_loadobj_t* lo, void* data) { - ServiceabilityAgentDbxModule* module = (ServiceabilityAgentDbxModule*) data; - char* name = readCStringFromProcess(lo->rl_nameaddr); - if (strstr(name, "libthread.so") != NULL) { - module->libThreadName = name; - return 0; - } else { - delete[] name; - return 1; - } -} - -bool -ServiceabilityAgentDbxModule::install() { - // NOTE interdependency between here and Java side wrapper - // FIXME: casts of string literal to char * to match prototype - _command = shell_imp_define_command((char *) "svc_agent_run", - &svc_agent_run, - 0, - NULL, - (char *) "Run the serviceability agent's dbx module.\n" - "This routine causes the module to listen on a socket for requests.\n" - "It does not return until the Java-side code tells it to exit, at\n" - "which point control is returned to the dbx shell."); - if (_command == NULL) { - debug_only(fprintf(stderr, "Serviceability agent: Failed to install svc_agent_run command\n")); - return false; - } - - // This is fairly painful. Since dbx doesn't currently load - // libthread_db with RTLD_GLOBAL, we can't just use RTLD_DEFAULT for - // the argument to dlsym. Instead, we have to use rtld_db to search - // through the loaded objects in the target process for libthread.so and - - // Try rtld_db - if (rd_init(RD_VERSION) != RD_OK) { - debug_only(fprintf(stderr, "Serviceability agent: Unable to init rtld_db\n")); - return false; - } - - rd_agent_t* rda = rd_new((struct ps_prochandle*) ps_get_prochandle2(1)); - if (rda == NULL) { - debug_only(fprintf(stderr, "Serviceability agent: Unable to allocate rtld_db agent\n")); - return false; - } - - if (rd_loadobj_iter(rda, (rl_iter_f*) findLibThreadCB, this) != RD_OK) { - debug_only(fprintf(stderr, "Serviceability agent: Loadobject iteration failed\n")); - return false; - } - - if (libThreadName == NULL) { - debug_only(fprintf(stderr, "Serviceability agent: Failed to find pathname to libthread.so in target process\n")); - return false; - } - - // Find and open libthread_db.so - char* slash = strrchr(libThreadName, '/'); - if (slash == NULL) { - debug_only(fprintf(stderr, "Serviceability agent: can't parse path to libthread.so \"%s\"\n")); - return false; - } - - int slashPos = slash - libThreadName; - char* buf = new char[slashPos + strlen("libthread_db.so") + 20]; // slop - if (buf == NULL) { - debug_only(fprintf(stderr, "Serviceability agent: error allocating libthread_db.so pathname\n")); - return false; - } - strncpy(buf, libThreadName, slashPos + 1); - - // Check dbx's data model; use sparcv9/ subdirectory if 64-bit and - // if target process is 32-bit - if ((sizeof(void*) == 8) && - (strstr(libThreadName, "sparcv9") == NULL)) { - strcpy(buf + slashPos + 1, "sparcv9/"); - slashPos += strlen("sparcv9/"); - } - - strcpy(buf + slashPos + 1, "libthread_db.so"); - - libThreadDB = dlopen(buf, RTLD_LAZY); - void* tmpDB = libThreadDB; - if (libThreadDB == NULL) { - debug_only(fprintf(stderr, "Serviceability agent: Warning: unable to find libthread_db.so at \"%s\"\n", buf)); - // Would like to handle this case as well. Maybe dbx has a better - // idea of where libthread_db.so lies. If the problem with dbx - // loading libthread_db without RTLD_GLOBAL specified ever gets - // fixed, we could run this code all the time. - tmpDB = RTLD_DEFAULT; - } - - delete[] buf; - - // Initialize access to libthread_db - td_init_fn = (td_init_fn_t*) dlsym(tmpDB, "td_init"); - td_ta_new_fn = (td_ta_new_fn_t*) dlsym(tmpDB, "td_ta_new"); - td_ta_delete_fn = (td_ta_delete_fn_t*) dlsym(tmpDB, "td_ta_delete"); - td_ta_map_id2thr_fn = (td_ta_map_id2thr_fn_t*) dlsym(tmpDB, "td_ta_map_id2thr"); - td_thr_getgregs_fn = (td_thr_getgregs_fn_t*) dlsym(tmpDB, "td_thr_getgregs"); - - if (td_init_fn == NULL || - td_ta_new_fn == NULL || - td_ta_delete_fn == NULL || - td_ta_map_id2thr_fn == NULL || - td_thr_getgregs_fn == NULL) { - debug_only(fprintf(stderr, "Serviceability agent: Failed to find one or more libthread_db symbols:\n")); - debug_only(if (td_init_fn == NULL) fprintf(stderr, " td_init\n")); - debug_only(if (td_ta_new_fn == NULL) fprintf(stderr, " td_ta_new\n")); - debug_only(if (td_ta_delete_fn == NULL) fprintf(stderr, " td_ta_delete\n")); - debug_only(if (td_ta_map_id2thr_fn == NULL) fprintf(stderr, " td_ta_map_id2thr\n")); - debug_only(if (td_thr_getgregs_fn == NULL) fprintf(stderr, " td_thr_getgregs\n")); - return false; - } - - if ((*td_init_fn)() != TD_OK) { - debug_only(fprintf(stderr, "Serviceability agent: Failed to initialize libthread_db\n")); - return false; - } - - return true; -} - -bool -ServiceabilityAgentDbxModule::uninstall() { - if (_command == NULL) { - return false; - } - - if (libThreadDB != NULL) { - dlclose(libThreadDB); - libThreadDB = NULL; - } - - int res = shell_imp_undefine_command(_command); - - if (res != 0) { - return false; - } - - return true; -} - -bool -ServiceabilityAgentDbxModule::run() { - // This is where most of the work gets done. - // The command processor loop looks like the following: - // - create listening socket - // - accept a connection (only one for now) - // - while that connection is open and the "exit" command has not - // been received: - // - read command - // - if it's the exit command, cleanup and return - // - otherwise, process command and write result - - int listening_socket = socket(AF_INET, SOCK_STREAM, 0); - if (listening_socket < 0) { - return false; - } - - // Set the SO_REUSEADDR property on the listening socket. This - // prevents problems with calls to bind() to the same port failing - // after this process exits. This seems to work on all platforms. - int reuse_address = 1; - if (setsockopt(listening_socket, SOL_SOCKET, SO_REUSEADDR, - (char *)&reuse_address, sizeof(reuse_address)) < 0) { - close(listening_socket); - return false; - } - - sockaddr_in server_address; - // Build the server address. We can bind the listening socket to the - // INADDR_ANY internet address. - memset((char*)&server_address, 0, sizeof(server_address)); - server_address.sin_family = AF_INET; - server_address.sin_addr.s_addr = (unsigned long)htonl(INADDR_ANY); - server_address.sin_port = htons((short)PORT); - - // Bind socket to port - if (bind(listening_socket, (sockaddr*) &server_address, - sizeof(server_address)) < 0) { - close(listening_socket); - return false; - } - - // Arbitrarily chosen backlog of 5 (shouldn't matter since we expect - // at most one connection) - if (listen(listening_socket, 5) < 0) { - close(listening_socket); - return false; - } - - // OK, now ready to wait for a data connection. This call to - // accept() will block. - struct sockaddr_in client_address; - int address_len = sizeof(client_address); - int client_socket = accept(listening_socket, (sockaddr*) &client_address, - &address_len); - // Close listening socket regardless of whether accept() succeeded. - // (FIXME: this may be annoying, especially during debugging, but I - // really feel that robustness and multiple connections should be - // handled higher up, e.g., at the Java level -- multiple clients - // could conceivably connect to the SA via RMI, and that would be a - // more robust solution than implementing multiple connections at - // this level) - NEEDS_CLEANUP; - - // NOTE: the call to shutdown() usually fails, so don't panic if this happens - shutdown(listening_socket, 2); - - if (close(listening_socket) < 0) { - debug_only(fprintf(stderr, "Serviceability agent: Error closing listening socket\n")); - return false; - } - - if (client_socket < 0) { - debug_only(fprintf(stderr, "Serviceability agent: Failed to open client socket\n")); - // No more cleanup necessary - return false; - } - - // Attempt to disable TCP buffering on this socket. We send small - // amounts of data back and forth and don't want buffering. - int buffer_val = 1; - if (setsockopt(client_socket, IPPROTO_IP, TCP_NODELAY, (char *) &buffer_val, sizeof(buffer_val)) < 0) { - debug_only(fprintf(stderr, "Serviceability agent: Failed to set TCP_NODELAY option on client socket\n")); - cleanup(client_socket); - return false; - } - - // OK, we have the data socket through which we will communicate - // with the Java side. Wait for commands or until reading or writing - // caused an error. - - bool should_continue = true; - - myComm.setSocket(client_socket); - -#ifdef PROFILING - scanTimer.reset(); - workTimer.reset(); - writeTimer.reset(); -#endif - - // Allocate a new thread agent for libthread_db - if ((*td_ta_new_fn)((ps_prochandle*) ps_get_prochandle2(1), &_tdb_agent) != - TD_OK) { - debug_only(fprintf(stderr, "Serviceability agent: Failed to allocate thread agent\n")); - cleanup(client_socket); - return false; - } - - do { - // Decided to use text to communicate between these processes. - // Probably will make debugging easier -- could telnet in if - // necessary. Will make scanning harder, but probably doesn't - // matter. - - // Why not just do what workshop does and parse dbx's console? - // Probably could do that, but at least this way we are in control - // of the text format on both ends. - - // FIXME: should have some way of synchronizing these commands - // between the C and Java sources. - - NEEDS_CLEANUP; - - // Do a blocking read of a line from the socket. - char *input_buffer = myComm.readLine(); - if (input_buffer == NULL) { - debug_only(fprintf(stderr, "Serviceability agent: error during read: errno = %d\n", errno)); - debug_only(perror("Serviceability agent")); - // Error occurred during read. - // FIXME: should guard against SIGPIPE - cleanup(client_socket); - return false; - } - - // OK, now ready to scan. See README-commands.txt for syntax - // descriptions. - - bool res = false; - if (!strncmp(input_buffer, CMD_ADDRESS_SIZE, strlen(CMD_ADDRESS_SIZE))) { - res = handleAddressSize(input_buffer + strlen(CMD_ADDRESS_SIZE)); - } else if (!strncmp(input_buffer, CMD_PEEK_FAIL_FAST, strlen(CMD_PEEK_FAIL_FAST))) { - res = handlePeekFailFast(input_buffer + strlen(CMD_PEEK_FAIL_FAST)); - } else if (!strncmp(input_buffer, CMD_PEEK, strlen(CMD_PEEK))) { - res = handlePeek(input_buffer + strlen(CMD_PEEK)); - } else if (!strncmp(input_buffer, CMD_POKE, strlen(CMD_POKE))) { - res = handlePoke(input_buffer + strlen(CMD_POKE)); - } else if (!strncmp(input_buffer, CMD_MAPPED, strlen(CMD_MAPPED))) { - res = handleMapped(input_buffer + strlen(CMD_MAPPED)); - } else if (!strncmp(input_buffer, CMD_LOOKUP, strlen(CMD_LOOKUP))) { - res = handleLookup(input_buffer + strlen(CMD_LOOKUP)); - } else if (!strncmp(input_buffer, CMD_THR_GREGS, strlen(CMD_THR_GREGS))) { - res = handleThrGRegs(input_buffer + strlen(CMD_THR_GREGS)); - } else if (!strncmp(input_buffer, CMD_EXIT, strlen(CMD_EXIT))) { - should_continue = false; - } - - if (should_continue) { - if (!res) { - cleanup(client_socket); - return false; - } - } - -#ifdef PROFILING - if (++numRequests == PROFILE_COUNT) { - fprintf(stderr, "%d requests: %d ms scanning, %d ms work, %d ms writing\n", - PROFILE_COUNT, scanTimer.total(), workTimer.total(), writeTimer.total()); - fflush(stderr); - scanTimer.reset(); - workTimer.reset(); - writeTimer.reset(); - numRequests = 0; - } -#endif - - } while (should_continue); - - // Successful exit - cleanup(client_socket); - return true; -} - -void -ServiceabilityAgentDbxModule::cleanup(int client_socket) { - shutdown(client_socket, 2); - close(client_socket); - if (_tdb_agent != NULL) { - (*td_ta_delete_fn)(_tdb_agent); - } -} - -bool -ServiceabilityAgentDbxModule::handleAddressSize(char* data) { - int data_model; - ps_err_e result = ps_pdmodel((ps_prochandle*) ps_get_prochandle2(1), - &data_model); - if (result != PS_OK) { - myComm.writeString("0"); - myComm.flush(); - return false; - } - - int val; - switch (data_model) { - case PR_MODEL_ILP32: - val = 32; - break; - case PR_MODEL_LP64: - val = 64; - break; - default: - val = 0; - break; - } - - if (!myComm.writeInt(val)) { - return false; - } - if (!myComm.writeEOL()) { - return false; - } - return myComm.flush(); -} - -bool -ServiceabilityAgentDbxModule::handlePeekFailFast(char* data) { - unsigned int val; - if (!scanUnsignedInt(&data, &val)) { - return false; - } - peek_fail_fast = (val ? true : false); - return true; -} - -bool -ServiceabilityAgentDbxModule::handlePeek(char* data) { - // Scan hex address, return false if failed - psaddr_t addr; -#ifdef PROFILING - scanTimer.start(); -#endif /* PROFILING */ - if (!scanAddress(&data, &addr)) { - return false; - } - unsigned int num; - if (!scanUnsignedInt(&data, &num)) { - return false; - } - if (num == 0) { -#ifdef PROFILING - writeTimer.start(); -#endif /* PROFILING */ - myComm.writeBinChar('B'); - myComm.writeBinChar(1); - myComm.writeBinUnsignedInt(0); - myComm.writeBinChar(0); -#ifdef PROFILING - writeTimer.stop(); -#endif /* PROFILING */ - return true; - } -#ifdef PROFILING - scanTimer.stop(); - workTimer.start(); -#endif /* PROFILING */ - char* buf = new char[num]; - ps_prochandle* cur_proc = (ps_prochandle*) ps_get_prochandle2(1); - ps_err_e result = ps_pread(cur_proc, addr, buf, num); - if (result == PS_OK) { - // Fast case; entire read succeeded. -#ifdef PROFILING - workTimer.stop(); - writeTimer.start(); -#endif /* PROFILING */ - myComm.writeBinChar('B'); - myComm.writeBinChar(1); - myComm.writeBinUnsignedInt(num); - myComm.writeBinChar(1); - myComm.writeBinBuf(buf, num); -#ifdef PROFILING - writeTimer.stop(); -#endif /* PROFILING */ - } else { -#ifdef PROFILING - workTimer.stop(); -#endif /* PROFILING */ - - if (peek_fail_fast) { -#ifdef PROFILING - writeTimer.start(); -#endif /* PROFILING */ - // Fail fast - myComm.writeBinChar('B'); - myComm.writeBinChar(1); - myComm.writeBinUnsignedInt(num); - myComm.writeBinChar(0); -#ifdef PROFILING - writeTimer.stop(); -#endif /* PROFILING */ - } else { - // Slow case: try to read one byte at a time - // FIXME: need better way of handling this, a la VirtualQuery - - unsigned int strideLen = 0; - int bufIdx = 0; - bool lastByteMapped = (ps_pread(cur_proc, addr, buf, 1) == PS_OK ? true : false); - -#ifdef PROFILING - writeTimer.start(); -#endif /* PROFILING */ - myComm.writeBinChar('B'); - myComm.writeBinChar(1); -#ifdef PROFILING - writeTimer.stop(); -#endif /* PROFILING */ - - for (int i = 0; i < num; ++i, ++addr) { -#ifdef PROFILING - workTimer.start(); -#endif /* PROFILING */ - result = ps_pread(cur_proc, addr, &buf[bufIdx], 1); -#ifdef PROFILING - workTimer.stop(); -#endif /* PROFILING */ - bool tmpMapped = (result == PS_OK ? true : false); -#ifdef PROFILING - writeTimer.start(); -#endif /* PROFILING */ - if (tmpMapped != lastByteMapped) { - // State change. Write the length of the last stride. - myComm.writeBinUnsignedInt(strideLen); - if (lastByteMapped) { - // Stop gathering data. Write the data of the last stride. - myComm.writeBinChar(1); - myComm.writeBinBuf(buf, strideLen); - bufIdx = 0; - } else { - // Start gathering data to write. - myComm.writeBinChar(0); - } - strideLen = 0; - lastByteMapped = tmpMapped; - } -#ifdef PROFILING - writeTimer.stop(); -#endif /* PROFILING */ - if (lastByteMapped) { - ++bufIdx; - } - ++strideLen; - } - - // Write last stride (must be at least one byte long by definition) -#ifdef PROFILING - writeTimer.start(); -#endif /* PROFILING */ - myComm.writeBinUnsignedInt(strideLen); - if (lastByteMapped) { - myComm.writeBinChar(1); - myComm.writeBinBuf(buf, strideLen); - } else { - myComm.writeBinChar(0); - } -#ifdef PROFILING - writeTimer.stop(); -#endif /* PROFILING */ - } - } - delete[] buf; - myComm.flush(); - return true; -} - -bool -ServiceabilityAgentDbxModule::handlePoke(char* data) { - // FIXME: not yet implemented - NEEDS_CLEANUP; - bool res = myComm.writeBoolAsInt(false); - myComm.flush(); - return res; -} - -bool -ServiceabilityAgentDbxModule::handleMapped(char* data) { - // Scan address - psaddr_t addr; - if (!scanAddress(&data, &addr)) { - return false; - } - unsigned int num; - if (!scanUnsignedInt(&data, &num)) { - return false; - } - unsigned char val; - ps_prochandle* cur_proc = (ps_prochandle*) ps_get_prochandle2(1); - char* buf = new char[num]; - if (ps_pread(cur_proc, addr, buf, num) == PS_OK) { - myComm.writeBoolAsInt(true); - } else { - myComm.writeBoolAsInt(false); - } - delete[] buf; - myComm.writeEOL(); - myComm.flush(); - return true; -} - -extern "C" -int loadobj_iterator(const rd_loadobj_t* loadobj, void *) { - if (loadobj != NULL) { - fprintf(stderr, "loadobj_iterator: visited loadobj \"%p\"\n", (void*) loadobj->rl_nameaddr); - return 1; - } - - fprintf(stderr, "loadobj_iterator: NULL loadobj\n"); - return 0; -} - -bool -ServiceabilityAgentDbxModule::handleLookup(char* data) { - // Debugging: iterate over loadobjs - /* - rd_agent_t* rld_agent = rd_new((ps_prochandle*) ps_get_prochandle2(1)); - rd_loadobj_iter(rld_agent, &loadobj_iterator, NULL); - rd_delete(rld_agent); - */ - -#ifdef PROFILING - scanTimer.start(); -#endif /* PROFILING */ - - char* object_name = scanSymbol(&data); - if (object_name == NULL) { - return false; - } - char* symbol_name = scanSymbol(&data); - if (symbol_name == NULL) { - delete[] object_name; - return false; - } - -#ifdef PROFILING - scanTimer.stop(); - workTimer.start(); -#endif /* PROFILING */ - - ps_sym_t sym; - // FIXME: check return values from write routines - ps_prochandle* process = (ps_prochandle*) ps_get_prochandle2(1); - ps_err_e lookup_res = ps_pglobal_sym(process, - object_name, symbol_name, &sym); -#ifdef PROFILING - workTimer.stop(); - writeTimer.start(); -#endif /* PROFILING */ - - delete[] object_name; - delete[] symbol_name; - if (lookup_res != PS_OK) { - // This is too noisy - // debug_only(fprintf(stderr, "ServiceabilityAgentDbxModule::handleLookup: error %d\n", lookup_res)); - myComm.writeString("0x0"); - } else { - myComm.writeAddress((void *)sym.st_value); - } - myComm.writeEOL(); - myComm.flush(); - -#ifdef PROFILING - writeTimer.stop(); -#endif /* PROFILING */ - - return true; -} - -bool -ServiceabilityAgentDbxModule::handleThrGRegs(char* data) { -#ifdef PROFILING - scanTimer.start(); -#endif /* PROFILING */ - - unsigned int num; - // Get the thread ID - if (!scanUnsignedInt(&data, &num)) { - return false; - } - -#ifdef PROFILING - scanTimer.stop(); - workTimer.start(); -#endif /* PROFILING */ - - // Map tid to thread handle - td_thrhandle_t thread_handle; - if ((*td_ta_map_id2thr_fn)(_tdb_agent, num, &thread_handle) != TD_OK) { - // fprintf(stderr, "Error mapping thread ID %d to thread handle\n", num); - return false; - } - - // Fetch register set - prgregset_t reg_set; - memset(reg_set, 0, sizeof(reg_set)); - td_err_e result = (*td_thr_getgregs_fn)(&thread_handle, reg_set); - if ((result != TD_OK) && (result != TD_PARTIALREG)) { - // fprintf(stderr, "Error fetching registers for thread handle %d: error = %d\n", num, result); - return false; - } - -#ifdef PROFILING - workTimer.stop(); - writeTimer.start(); -#endif /* PROFILING */ - -#if (defined(__sparc) || defined(__i386)) - myComm.writeInt(NPRGREG); - myComm.writeSpace(); - for (int i = 0; i < NPRGREG; i++) { - myComm.writeAddress((void *)reg_set[i]); - if (i == NPRGREG - 1) { - myComm.writeEOL(); - } else { - myComm.writeSpace(); - } - } -#else -#error Please port ServiceabilityAgentDbxModule::handleThrGRegs to your current platform -#endif - - myComm.flush(); - -#ifdef PROFILING - writeTimer.stop(); -#endif /* PROFILING */ - - return true; -} - -// -// Input routines -// - -bool -ServiceabilityAgentDbxModule::scanAddress(char** data, psaddr_t* addr) { - *addr = 0; - - // Skip whitespace - while ((**data != 0) && (isspace(**data))) { - ++*data; - } - - if (**data == 0) { - return false; - } - - if (strncmp(*data, "0x", 2) != 0) { - return false; - } - - *data += 2; - - while ((**data != 0) && (!isspace(**data))) { - int val; - bool res = charToNibble(**data, &val); - if (!res) { - return false; - } - *addr <<= 4; - *addr |= val; - ++*data; - } - - return true; -} - -bool -ServiceabilityAgentDbxModule::scanUnsignedInt(char** data, unsigned int* num) { - *num = 0; - - // Skip whitespace - while ((**data != 0) && (isspace(**data))) { - ++*data; - } - - if (**data == 0) { - return false; - } - - while ((**data != 0) && (!isspace(**data))) { - char cur = **data; - if ((cur < '0') || (cur > '9')) { - return false; - } - *num *= 10; - *num += cur - '0'; - ++*data; - } - - return true; -} - -char* -ServiceabilityAgentDbxModule::scanSymbol(char** data) { - // Skip whitespace - while ((**data != 0) && (isspace(**data))) { - ++*data; - } - - if (**data == 0) { - return NULL; - } - - // First count length - int len = 1; // Null terminator - char* tmpData = *data; - while ((*tmpData != 0) && (!isspace(*tmpData))) { - ++tmpData; - ++len; - } - char* buf = new char[len]; - strncpy(buf, *data, len - 1); - buf[len - 1] = 0; - *data += len - 1; - return buf; -} - -bool -ServiceabilityAgentDbxModule::charToNibble(char ascii, int* value) { - if (ascii >= '0' && ascii <= '9') { - *value = ascii - '0'; - return true; - } else if (ascii >= 'A' && ascii <= 'F') { - *value = 10 + ascii - 'A'; - return true; - } else if (ascii >= 'a' && ascii <= 'f') { - *value = 10 + ascii - 'a'; - return true; - } - - return false; -} - - -char* -ServiceabilityAgentDbxModule::readCStringFromProcess(psaddr_t addr) { - char c; - int num = 0; - ps_prochandle* cur_proc = (ps_prochandle*) ps_get_prochandle2(1); - - // Search for null terminator - do { - if (ps_pread(cur_proc, addr + num, &c, 1) != PS_OK) { - return NULL; - } - ++num; - } while (c != 0); - - // Allocate string - char* res = new char[num]; - if (ps_pread(cur_proc, addr, res, num) != PS_OK) { - delete[] res; - return NULL; - } - return res; -} - - -//-------------------------------------------------------------------------------- -// Class Timer -// - -Timer::Timer() { - reset(); -} - -Timer::~Timer() { -} - -void -Timer::start() { - gettimeofday(&startTime, NULL); -} - -void -Timer::stop() { - struct timeval endTime; - gettimeofday(&endTime, NULL); - totalMicroseconds += timevalDiff(&startTime, &endTime); - ++counter; -} - -long -Timer::total() { - return (totalMicroseconds / 1000); -} - -long -Timer::average() { - return (long) ((double) total() / (double) counter); -} - -void -Timer::reset() { - totalMicroseconds = 0; - counter = 0; -} - -long long -Timer::timevalDiff(struct timeval* start, struct timeval* end) { - long long secs = end->tv_sec - start->tv_sec; - secs *= 1000000; - long long usecs = end->tv_usec - start->tv_usec; - return (secs + usecs); -}