#include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Profiler: A profiler for the Linux Java JDK1.2 that reports time * spent in native code. Useful for understanding how much time is * spent in various libraries. * * Author: Nathan Meyers, nmeyers@javalinux.net * $Id: profiler.C,v 1.24 1999/11/07 20:18:21 nathanm Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * with this program. If not, the license is available from the * GNU project, at http://www.gnu.org. */ // Our main profiler class, which sets up, starts, and stops the profiler. // Everything in this class is static... the static functions are usable // with the C interfaces required by JVMPI. class profiler { public: static JVMPI_Interface *jvmpi_interface; static unsigned long size, user_scale, scale, expand; static unsigned long low_address; static unsigned long high_address; static unsigned long address_range; static unsigned long array_size; static int expand_is_percent, global; static string filename; static jint JVM_OnLoad(JavaVM *jvm, char *options); static void NotifyEvent(JVMPI_Event *event); static jint usage(); static ostream *outfile; static struct tms initial_times; }; // This class encapsulates data on hot spots class hotspot { public: string label; unsigned int count; hotspot() : label(""), count(0) {} hotspot(string s, unsigned int c) : label(s), count(c) {} hotspot& operator=(const hotspot &src) { label = src.label; count = src.count; return *this; } friend int operator==(const hotspot &lhs, const hotspot &rhs) { return lhs.label == rhs.label; } static int compare(const hotspot &lhs, const hotspot &rhs) { return lhs.count > rhs.count; } hotspot& operator+=(const hotspot &hs) { count += hs.count; return *this; } }; // This class encapsulates a line of data from the /proc//maps file // describing the address range covered by a mapped shared library class shared_library { shared_library() : lib_total_time(0), lib_no_procedure_time(0), nm_pipe(0), current_addr(0), next_addr(0), current_proc(""), next_proc("") {} public: unsigned long range_start, range_end; unsigned long lib_total_time; unsigned long lib_no_procedure_time; string filename; static shared_library *factory(string &); istream *nm_pipe; string current_proc, next_proc; unsigned long current_addr, next_addr; static int compare(shared_library *a, shared_library *b) { return a->range_start < b->range_start; } static int compare2(shared_library *a, shared_library *b) { return a->lib_total_time > b->lib_total_time; } void charge_time(unsigned long, unsigned long, unsigned int, int); vector hotspots; void cleanup() { if (nm_pipe) delete nm_pipe; nm_pipe = 0; } ~shared_library() { cleanup(); } }; // This class encapsulates data read from the /proc//maps file class memory_map { public: vector libraries; memory_map(); ~memory_map(); }; // External entry point to match JVMPI's requirements - calls a static // class function to handle initialization. extern "C" jint JNICALL JVM_OnLoad(JavaVM *jvm, char *options, void *) { return profiler::JVM_OnLoad(jvm, options); } JVMPI_Interface *profiler::jvmpi_interface; unsigned long profiler::size = 0; unsigned long profiler::user_scale = 1024; unsigned long profiler::scale; unsigned long profiler::expand = 0; unsigned long profiler::low_address; unsigned long profiler::high_address; unsigned long profiler::address_range; unsigned long profiler::array_size; ostream *profiler::outfile; struct tms profiler::initial_times; int profiler::expand_is_percent = 0; int profiler::global = 1; string profiler::filename = ""; u_short *profile_buffer = 0; // Handle initialization: load the JVMPI pointer, parse args, set up // profiling. jint profiler::JVM_OnLoad(JavaVM *jvm, char *options) { // Get our JVM interface pointer if (jvm->GetEnv((void **)&jvmpi_interface, JVMPI_VERSION_1) < 0) return JNI_ERR; // Set up pointer to our NotifyEvent function jvmpi_interface->NotifyEvent = NotifyEvent; // Initialize output file outfile = &cerr; // Legal options: // size=<#bytes> Allocate #bytes for the profile array // scale= Map bytes per profile array entry // expand=<#bytes> Allow for <#bytes> growth of shared lib space // expand=% Allow for grown of shared lib space // scope=local Report local & global addresses // scope=global Report global addresses (default) // help while (options && *options) { char *p2; if (!strncmp(options, "file=", 5)) { options += 5; p2 = strchr(options, ','); int len = p2 ? p2 - options : strlen(options); if (!len) return usage(); filename = options; filename.erase(len); options = p2 ? p2 : options + len; } else if (!strncmp(options, "size=", 5)) { options += 5; size = strtol(options, &p2, 0); if (p2 == options) return usage(); options = p2; } else if (!strncmp(options, "scale=", 6)) { options += 6; user_scale = strtol(options, &p2, 0); if (user_scale < 1) user_scale = 1; if (p2 == options) return usage(); options = p2; } else if (!strncmp(options, "expand=", 7)) { options += 7; expand = strtol(options, &p2, 0); if (p2 == options) return usage(); options = p2; if ((expand_is_percent = (*options == '%'))) options++; } else if (!strncmp(options, "scope=local", 11)) { options += 11; global = 0; } else if (!strncmp(options, "scope=global", 12)) { options += 12; global = 1; } else return usage(); if (*options && *options++ != ',') return usage(); } if (filename.length()) { outfile = new ofstream(filename.c_str()); if (outfile->fail()) { cerr << "Cannot open " << filename << " for output\n"; return JNI_ERR; } } // Indicate that we want only the init-done and shutdown events jvmpi_interface->EnableEvent(JVMPI_EVENT_JVM_INIT_DONE, 0); jvmpi_interface->EnableEvent(JVMPI_EVENT_JVM_SHUT_DOWN, 0); } jint profiler::usage() { cerr << "Profiler usage: -XrunProfiler:[