Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
SerCeMan committed Jun 18, 2016
0 parents commit 50eed3d
Show file tree
Hide file tree
Showing 18 changed files with 1,082 additions and 0 deletions.
44 changes: 44 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
### Intellij ###
.idea
*.iws
/out/
*.iml
modules.xml

### Java ###
*.class
*.jar

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*


### Gradle ###
.gradle
build/

# Ignore Gradle GUI config
gradle-app.setting

# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar

# Cache of project
.gradletasknamecache

# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
# gradle/wrapper/gradle-wrapper.properties


### Project ###
lib/
franky-proto/generated

### CMake ###
CMakeCache.txt
CMakeFiles
CMakeScripts
Makefile
cmake_install.cmake
install_manifest.txt
CTestTestfile.cmake
26 changes: 26 additions & 0 deletions async-profiler/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 3.5)
project(franky)

set(JAVA_HOME /usr/lib/jvm/java-8-oracle)
set(LIB_NAME libasyncProfiler.so)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")

include_directories(
${JAVA_HOME}/include
${JAVA_HOME}/include/linux
${CMAKE_CURRENT_BINARY_DIR})

set(SOURCE_FILES
src/profiler.cpp
src/profiler.h
src/vmEntry.cpp
src/vmEntry.h)

ADD_SUBDIRECTORY(proto)


set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../lib)

add_library(frankyagent SHARED ${SOURCE_FILES})
TARGET_LINK_LIBRARIES(frankyagent proto ${PROTOBUF_LIBRARY})
1 change: 1 addition & 0 deletions async-profiler/proto
307 changes: 307 additions & 0 deletions async-profiler/src/profiler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,307 @@
/*
* Copyright 2016 Andrei Pangin
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <stdio.h>
#include <stdlib.h>
#include <cstdint>
#include <string.h>
#include <signal.h>
#include <string>
#include <sys/time.h>

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#include "profiler.h"
#include "vmEntry.h"


Profiler Profiler::_instance;


static void sigprofHandler(int signo, siginfo_t *siginfo, void *ucontext) {
Profiler::_instance.recordSample(ucontext);
}


MethodName::MethodName(jmethodID method) {
jclass method_class;
jvmtiEnv *jvmti = VM::jvmti();
jvmti->GetMethodName(method, &_name, &_sig, NULL);
jvmti->GetMethodDeclaringClass(method, &method_class);
jvmti->GetClassSignature(method_class, &_class_sig, NULL);

char *s;
for (s = _class_sig; *s; s++) {
if (*s == '/') *s = '.';
}
s[-1] = 0;
}

MethodName::~MethodName() {
jvmtiEnv *jvmti = VM::jvmti();
jvmti->Deallocate((unsigned char *) _name);
jvmti->Deallocate((unsigned char *) _sig);
jvmti->Deallocate((unsigned char *) _class_sig);
}


void CallTraceSample::assign(ASGCT_CallTrace *trace) {
_call_count = 1;
_num_frames = trace->num_frames;
for (int i = 0; i < trace->num_frames; i++) {
_frames[i] = trace->frames[i];
}
}


uint64_t Profiler::hashCallTrace(ASGCT_CallTrace *trace) {
const uint64_t M = 0xc6a4a7935bd1e995LL;
const int R = 47;

uint64_t h = trace->num_frames * M;

for (int i = 0; i < trace->num_frames; i++) {
uint64_t k = ((uint64_t) trace->frames[i].bci << 32) ^(uint64_t) trace->frames[i].method_id;
k *= M;
k ^= k >> R;
k *= M;
h ^= k;
h *= M;
}

h ^= h >> R;
h *= M;
h ^= h >> R;

return h;
}

void Profiler::storeCallTrace(ASGCT_CallTrace *trace) {
uint64_t hash = hashCallTrace(trace);
int bucket = (int) (hash % MAX_CALLTRACES);
int i = bucket;

do {
if (_hashes[i] == hash && _traces[i]._call_count > 0) {
_traces[i]._call_count++;
return;
} else if (_hashes[i] == 0) {
break;
}
if (++i == MAX_CALLTRACES) i = 0;
} while (i != bucket);

_hashes[i] = hash;
_traces[i].assign(trace);
}

uint64_t Profiler::hashMethod(jmethodID method) {
const uint64_t M = 0xc6a4a7935bd1e995LL;
const int R = 17;

uint64_t h = (uint64_t) method;

h ^= h >> R;
h *= M;
h ^= h >> R;

return h;
}

void Profiler::storeMethod(jmethodID method) {
uint64_t hash = hashMethod(method);
int bucket = (int) (hash % MAX_CALLTRACES);
int i = bucket;

do {
if (_methods[i]._method == method) {
_methods[i]._call_count++;
return;
} else if (_methods[i]._method == NULL) {
break;
}
if (++i == MAX_CALLTRACES) i = 0;
} while (i != bucket);

_methods[i]._call_count = 1;
_methods[i]._method = method;
}

void Profiler::recordSample(void *ucontext) {
_calls_total++;

JNIEnv *jni = VM::jni();
if (jni == NULL) {
_calls_non_java++;
return;
}

ASGCT_CallFrame frames[MAX_FRAMES];
ASGCT_CallTrace trace = {jni, MAX_FRAMES, frames};
AsyncGetCallTrace(&trace, trace.num_frames, ucontext);

if (trace.num_frames > 0) {
storeCallTrace(&trace);
storeMethod(frames[0].method_id);
} else if (trace.num_frames == -2) {
_calls_gc++;
} else if (trace.num_frames == -9) {
_calls_deopt++;
} else {
_calls_unknown++;
}
}

void Profiler::setTimer(long sec, long usec) {
bool enabled = sec | usec;

struct sigaction sa;
sa.sa_handler = enabled ? NULL : SIG_IGN;
sa.sa_sigaction = enabled ? sigprofHandler : NULL;
sa.sa_flags = SA_RESTART | SA_SIGINFO;
sigemptyset(&sa.sa_mask);
sigaction(SIGPROF, &sa, NULL);

struct itimerval itv = {{sec, usec},
{sec, usec}};
setitimer(ITIMER_PROF, &itv, NULL);
}

void Profiler::start(long interval) {
if (_running) return;
_running = true;

_calls_total = _calls_non_java = _calls_gc = _calls_deopt = _calls_unknown = 0;
memset(_hashes, 0, sizeof(_hashes));
memset(_traces, 0, sizeof(_traces));
memset(_methods, 0, sizeof(_methods));

setTimer(interval / 1000, interval * 1000);
}

void Profiler::stop() {
if (!_running) return;
_running = false;

setTimer(0, 0);
}

void Profiler::summary(std::ostream &out) {
float percent = 100.0f / _calls_total;
char buf[256];
sprintf(buf,
"--- Execution profile ---\n"
"Total: %d\n"
"Non-Java: %d (%.2f%%)\n"
"GC: %d (%.2f%%)\n"
"Deopt: %d (%.2f%%)\n"
"Unknown: %d (%.2f%%)\n"
"\n",
_calls_total,
_calls_non_java, _calls_non_java * percent,
_calls_gc, _calls_gc * percent,
_calls_deopt, _calls_deopt * percent,
_calls_unknown, _calls_unknown * percent
);
out << buf;
}

void Profiler::dumpTraces(std::ostream &out, int max_traces) {
if (_running) return;

float percent = 100.0f / _calls_total;
char buf[1024];

qsort(_traces, MAX_CALLTRACES, sizeof(CallTraceSample), CallTraceSample::comparator);
if (max_traces > MAX_CALLTRACES) max_traces = MAX_CALLTRACES;

for (int i = 0; i < max_traces; i++) {
int samples = _traces[i]._call_count;
if (samples == 0) break;

sprintf(buf, "Samples: %d (%.2f%%)\n", samples, samples * percent);
out << buf;

for (int j = 0; j < _traces[i]._num_frames; j++) {
ASGCT_CallFrame *frame = &_traces[i]._frames[j];
if (frame->method_id != NULL) {
MethodName mn(frame->method_id);
sprintf(buf, " [%2d] %s.%s @%d\n", j, mn.holder(), mn.name(), frame->bci);
out << buf;
}
}
out << "\n";
}
}

void Profiler::dumpMethods(std::ostream &out) {
if (_running) return;

float percent = 100.0f / _calls_total;
char buf[1024];

qsort(_methods, MAX_CALLTRACES, sizeof(MethodSample), MethodSample::comparator);

for (int i = 0; i < MAX_CALLTRACES; i++) {
int samples = _methods[i]._call_count;
if (samples == 0) break;

MethodName mn(_methods[i]._method);
sprintf(buf, "%6d (%.2f%%) %s.%s\n", samples, samples * percent, mn.holder(), mn.name());
out << buf;
}
}

void error(const char *msg) {
std::string res = std::string("ERROR") + std::string(msg);
perror(res.c_str());
exit(0);
}


void Profiler::init(int port) {
portno = (uint16_t) port;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
error("opening socket");
}
server = gethostbyname("localhost");
if (server == NULL) {
error("unable to connect to localhost");
}
struct sockaddr_in serv_addr;
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy(server->h_addr, (char *) &serv_addr.sin_addr.s_addr, server->h_length);
serv_addr.sin_port = htons(portno);
if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
error("connecting");
}

int n;
char buffer[256] = "Hello";

n = write(sockfd, buffer, strlen(buffer));
if (n < 0)
error("ERROR writing to socket");
close(sockfd);
}


Loading

0 comments on commit 50eed3d

Please sign in to comment.