1 /* 2 * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * - Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * - Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * - Neither the name of Oracle nor the names of its 16 * contributors may be used to endorse or promote products derived 17 * from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * This source code is provided to illustrate the usage of a given feature 34 * or technique and has been deliberately simplified. Additional steps 35 * required for a production-quality application, such as security checks, 36 * input validation and proper error handling, might not be present in 37 * this sample code. 38 */ 39 40 41 /* The hprof listener loop thread. net=hostname:port option */ 42 43 /* 44 * The option net=hostname:port causes all hprof output to be sent down 45 * a socket connection, and also allows for commands to come in over the 46 * socket. The commands are documented below. 47 * 48 * This thread can cause havoc when started prematurely or not terminated 49 * properly, see listener_init() and listener_term(), and their calls 50 * in hprof_init.c. 51 * 52 * The listener loop (hprof_listener.c) can dynamically turn on or off the 53 * sampling of all or selected threads. 54 * 55 * The specification of this command protocol is only here, in the comments 56 * below. The HAT tools uses this interface. 57 * It is also unknown how well these options work given the limited 58 * testing of this interface. 59 * 60 */ 61 62 #include "hprof.h" 63 64 /* When the hprof Agent in the VM is connected via a socket to the 65 * profiling client, the client may send the hprof Agent a set of commands. 66 * The commands have the following format: 67 * 68 * u1 a TAG denoting the type of the record 69 * u4 a serial number 70 * u4 number of bytes *remaining* in the record. Note that 71 * this number excludes the tag and the length field itself. 72 * [u1]* BODY of the record (a sequence of bytes) 73 */ 74 75 /* The following commands are presently supported: 76 * 77 * TAG BODY notes 78 * ---------------------------------------------------------- 79 * HPROF_CMD_GC force a GC. 80 * 81 * HPROF_CMD_DUMP_HEAP obtain a heap dump 82 * 83 * HPROF_CMD_ALLOC_SITES obtain allocation sites 84 * 85 * u2 flags 0x0001: incremental vs. complete 86 * 0x0002: sorted by allocation vs. live 87 * 0x0004: whether to force a GC 88 * u4 cutoff ratio (0.0 ~ 1.0) 89 * 90 * HPROF_CMD_HEAP_SUMMARY obtain heap summary 91 * 92 * HPROF_CMD_DUMP_TRACES obtain all newly created traces 93 * 94 * HPROF_CMD_CPU_SAMPLES obtain a HPROF_CPU_SAMPLES record 95 * 96 * u2 ignored for now 97 * u4 cutoff ratio (0.0 ~ 1.0) 98 * 99 * HPROF_CMD_CONTROL changing settings 100 * 101 * u2 0x0001: alloc traces on 102 * 0x0002: alloc traces off 103 * 104 * 0x0003: CPU sampling on 105 * 106 * id: thread object id (NULL for all) 107 * 108 * 0x0004: CPU sampling off 109 * 110 * id: thread object id (NULL for all) 111 * 112 * 0x0005: CPU sampling clear 113 * 114 * 0x0006: clear alloc sites info 115 * 116 * 0x0007: set max stack depth in CPU samples 117 * and alloc traces 118 * 119 * u2: new depth 120 */ 121 122 typedef enum HprofCmd { 123 HPROF_CMD_GC = 0x01, 124 HPROF_CMD_DUMP_HEAP = 0x02, 125 HPROF_CMD_ALLOC_SITES = 0x03, 126 HPROF_CMD_HEAP_SUMMARY = 0x04, 127 HPROF_CMD_EXIT = 0x05, 128 HPROF_CMD_DUMP_TRACES = 0x06, 129 HPROF_CMD_CPU_SAMPLES = 0x07, 130 HPROF_CMD_CONTROL = 0x08, 131 HPROF_CMD_EOF = 0xFF 132 } HprofCmd; 133 134 static jint 135 recv_fully(int f, char *buf, int len) 136 { 137 jint nbytes; 138 139 nbytes = 0; 140 if ( f < 0 ) { 141 return nbytes; 142 } 143 while (nbytes < len) { 144 int res; 145 146 res = md_recv(f, buf + nbytes, (len - nbytes), 0); 147 if (res < 0) { 148 /* 149 * hprof was disabled before we returned from recv() above. 150 * This means the command socket is closed so we let that 151 * trickle back up the command processing stack. 152 */ 153 LOG("recv() returned < 0"); 154 break; 155 } 156 nbytes += res; 157 } 158 return nbytes; 159 } 160 161 static unsigned char 162 recv_u1(void) 163 { 164 unsigned char c; 165 jint nbytes; 166 167 nbytes = recv_fully(gdata->fd, (char *)&c, (int)sizeof(unsigned char)); 168 if (nbytes == 0) { 169 c = HPROF_CMD_EOF; 170 } 171 return c; 172 } 173 174 static unsigned short 175 recv_u2(void) 176 { 177 unsigned short s; 178 jint nbytes; 179 180 nbytes = recv_fully(gdata->fd, (char *)&s, (int)sizeof(unsigned short)); 181 if (nbytes == 0) { 182 s = (unsigned short)-1; 183 } 184 return md_ntohs(s); 185 } 186 187 static unsigned 188 recv_u4(void) 189 { 190 unsigned i; 191 jint nbytes; 192 193 nbytes = recv_fully(gdata->fd, (char *)&i, (int)sizeof(unsigned)); 194 if (nbytes == 0) { 195 i = (unsigned)-1; 196 } 197 return md_ntohl(i); 198 } 199 200 static ObjectIndex 201 recv_id(void) 202 { 203 ObjectIndex result; 204 jint nbytes; 205 206 nbytes = recv_fully(gdata->fd, (char *)&result, (int)sizeof(ObjectIndex)); 207 if (nbytes == 0) { 208 result = (ObjectIndex)0; 209 } 210 return result; 211 } 212 213 static void JNICALL 214 listener_loop_function(jvmtiEnv *jvmti, JNIEnv *env, void *p) 215 { 216 jboolean keep_processing; 217 unsigned char tag; 218 jboolean kill_the_whole_process; 219 220 kill_the_whole_process = JNI_FALSE; 221 tag = 0; 222 223 rawMonitorEnter(gdata->listener_loop_lock); { 224 gdata->listener_loop_running = JNI_TRUE; 225 keep_processing = gdata->listener_loop_running; 226 /* Tell listener_init() that we have started */ 227 rawMonitorNotifyAll(gdata->listener_loop_lock); 228 } rawMonitorExit(gdata->listener_loop_lock); 229 230 while ( keep_processing ) { 231 232 LOG("listener loop iteration"); 233 234 tag = recv_u1(); /* This blocks here on the socket read, a close() 235 * on this fd will wake this up. And if recv_u1() 236 * can't read anything, it returns HPROF_CMD_EOF. 237 */ 238 239 LOG3("listener_loop", "command = ", tag); 240 241 if (tag == HPROF_CMD_EOF) { 242 /* The cmd socket has closed so the listener thread is done 243 * just fall out of loop and let the thread die. 244 */ 245 keep_processing = JNI_FALSE; 246 break; 247 } 248 249 /* seq_num not used */ 250 (void)recv_u4(); 251 /* length not used */ 252 (void)recv_u4(); 253 254 switch (tag) { 255 case HPROF_CMD_GC: 256 runGC(); 257 break; 258 case HPROF_CMD_DUMP_HEAP: { 259 site_heapdump(env); 260 break; 261 } 262 case HPROF_CMD_ALLOC_SITES: { 263 unsigned short flags; 264 unsigned i_tmp; 265 float ratio; 266 267 flags = recv_u2(); 268 i_tmp = recv_u4(); 269 ratio = *(float *)(&i_tmp); 270 site_write(env, flags, ratio); 271 break; 272 } 273 case HPROF_CMD_HEAP_SUMMARY: { 274 rawMonitorEnter(gdata->data_access_lock); { 275 io_write_heap_summary( gdata->total_live_bytes, 276 gdata->total_live_instances, 277 gdata->total_alloced_bytes, 278 gdata->total_alloced_instances); 279 } rawMonitorExit(gdata->data_access_lock); 280 break; 281 } 282 case HPROF_CMD_EXIT: 283 keep_processing = JNI_FALSE; 284 kill_the_whole_process = JNI_TRUE; 285 verbose_message("HPROF: received exit event, exiting ...\n"); 286 break; 287 case HPROF_CMD_DUMP_TRACES: 288 rawMonitorEnter(gdata->data_access_lock); { 289 trace_output_unmarked(env); 290 } rawMonitorExit(gdata->data_access_lock); 291 break; 292 case HPROF_CMD_CPU_SAMPLES: { 293 unsigned i_tmp; 294 float ratio; 295 296 /* flags not used */ 297 (void)recv_u2(); 298 i_tmp = recv_u4(); 299 ratio = *(float *)(&i_tmp); 300 trace_output_cost(env, ratio); 301 break; 302 } 303 case HPROF_CMD_CONTROL: { 304 unsigned short cmd = recv_u2(); 305 if (cmd == 0x0001) { 306 setEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_OBJECT_FREE, NULL); 307 tracker_engage(env); 308 } else if (cmd == 0x0002) { 309 setEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_OBJECT_FREE, NULL); 310 tracker_disengage(env); 311 } else if (cmd == 0x0003) { 312 ObjectIndex thread_object_index; 313 thread_object_index = recv_id(); 314 cpu_sample_on(env, thread_object_index); 315 } else if (cmd == 0x0004) { 316 ObjectIndex thread_object_index; 317 thread_object_index = recv_id(); 318 cpu_sample_off(env, thread_object_index); 319 } else if (cmd == 0x0005) { 320 rawMonitorEnter(gdata->data_access_lock); { 321 trace_clear_cost(); 322 } rawMonitorExit(gdata->data_access_lock); 323 } else if (cmd == 0x0006) { 324 rawMonitorEnter(gdata->data_access_lock); { 325 site_cleanup(); 326 site_init(); 327 } rawMonitorExit(gdata->data_access_lock); 328 } else if (cmd == 0x0007) { 329 gdata->max_trace_depth = recv_u2(); 330 } 331 break; 332 } 333 default:{ 334 char buf[80]; 335 336 keep_processing = JNI_FALSE; 337 kill_the_whole_process = JNI_TRUE; 338 (void)md_snprintf(buf, sizeof(buf), 339 "failed to recognize cmd %d, exiting..", (int)tag); 340 buf[sizeof(buf)-1] = 0; 341 HPROF_ERROR(JNI_FALSE, buf); 342 break; 343 } 344 } 345 346 rawMonitorEnter(gdata->data_access_lock); { 347 io_flush(); 348 } rawMonitorExit(gdata->data_access_lock); 349 350 rawMonitorEnter(gdata->listener_loop_lock); { 351 if ( !gdata->listener_loop_running ) { 352 keep_processing = JNI_FALSE; 353 } 354 } rawMonitorExit(gdata->listener_loop_lock); 355 356 } 357 358 /* If listener_term() is causing this loop to terminate, then 359 * you will block here until listener_term wants you to proceed. 360 */ 361 rawMonitorEnter(gdata->listener_loop_lock); { 362 if ( gdata->listener_loop_running ) { 363 /* We are terminating for our own reasons, maybe because of 364 * EOF (socket closed?), or EXIT request, or invalid command. 365 * Not from listener_term(). 366 * We set gdata->listener_loop_running=FALSE so that any 367 * future call to listener_term() will do nothing. 368 */ 369 gdata->listener_loop_running = JNI_FALSE; 370 } else { 371 /* We assume that listener_term() is stopping us, 372 * now we need to tell it we understood. 373 */ 374 rawMonitorNotifyAll(gdata->listener_loop_lock); 375 } 376 } rawMonitorExit(gdata->listener_loop_lock); 377 378 LOG3("listener_loop", "finished command = ", tag); 379 380 /* If we got an explicit command request to die, die here */ 381 if ( kill_the_whole_process ) { 382 error_exit_process(0); 383 } 384 385 } 386 387 /* External functions */ 388 389 void 390 listener_init(JNIEnv *env) 391 { 392 /* Create the raw monitor */ 393 gdata->listener_loop_lock = createRawMonitor("HPROF listener lock"); 394 395 rawMonitorEnter(gdata->listener_loop_lock); { 396 createAgentThread(env, "HPROF listener thread", 397 &listener_loop_function); 398 /* Wait for listener_loop_function() to tell us it started. */ 399 rawMonitorWait(gdata->listener_loop_lock, 0); 400 } rawMonitorExit(gdata->listener_loop_lock); 401 } 402 403 void 404 listener_term(JNIEnv *env) 405 { 406 rawMonitorEnter(gdata->listener_loop_lock); { 407 408 /* If we are in the middle of sending bytes down the socket, this 409 * at least keeps us blocked until that processing is done. 410 */ 411 rawMonitorEnter(gdata->data_access_lock); { 412 413 /* Make sure the socket gets everything */ 414 io_flush(); 415 416 /* 417 * Graceful shutdown of the socket will assure that all data 418 * sent is received before the socket close completes. 419 */ 420 (void)md_shutdown(gdata->fd, 2 /* disallow sends and receives */); 421 422 /* This close will cause the listener loop to possibly wake up 423 * from the recv_u1(), this is critical to get thread running again. 424 */ 425 md_close(gdata->fd); 426 } rawMonitorExit(gdata->data_access_lock); 427 428 /* It could have shut itself down, so we check the global flag */ 429 if ( gdata->listener_loop_running ) { 430 /* It stopped because of something listener_term() did. */ 431 gdata->listener_loop_running = JNI_FALSE; 432 /* Wait for listener_loop_function() to tell us it finished. */ 433 rawMonitorWait(gdata->listener_loop_lock, 0); 434 } 435 } rawMonitorExit(gdata->listener_loop_lock); 436 }