comparison agent/src/os/solaris/dbx/svc_agent_dbx.cpp @ 0:a61af66fc99e jdk7-b24

Initial load
author duke
date Sat, 01 Dec 2007 00:00:00 +0000
parents
children c18cbe5936b8
comparison
equal deleted inserted replaced
-1:000000000000 0:a61af66fc99e
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 }