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 }