1 /* 2 * Copyright 2000-2002 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 20 * CA 95054 USA or visit www.sun.com if you need additional information or 21 * have any 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 }