Mercurial > hg > truffle
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 } |