Skip to content

Instantly share code, notes, and snippets.

@apangin
Last active April 11, 2025 01:00
Show Gist options
  • Save apangin/28e24cd5d4aa235c329884f0cfb4868e to your computer and use it in GitHub Desktop.
Save apangin/28e24cd5d4aa235c329884f0cfb4868e to your computer and use it in GitHub Desktop.
JVM TI agent that continuously bombards C2 Compiler threads with no-op signals
#define _GNU_SOURCE
#include <jvmti.h>
#include <dirent.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>
#include <unistd.h>
static int is_compiler_thread(int tid) {
char buf[64];
snprintf(buf, sizeof(buf), "/proc/self/task/%d/comm", tid);
int fd = open(buf, O_RDONLY);
if (fd == -1) {
return 0;
}
ssize_t r = read(fd, buf, sizeof(buf));
close(fd);
return r > 0 && strncmp(buf, "C2 Compiler", 11) == 0;
}
static void* signaling_loop(void* unused) {
pid_t self = getpid();
unsigned int counter = 0;
DIR* dir = opendir("/proc/self/task");
if (dir == NULL) {
return NULL;
}
while (1) {
struct dirent* entry;
while ((entry = readdir(dir)) != NULL) {
if (entry->d_name[0] != '.') {
int tid = atoi(entry->d_name);
if (is_compiler_thread(tid)) {
syscall(__NR_tgkill, self, tid, SIGPROF);
}
}
}
rewinddir(dir);
}
}
#define BUF_SIZE 262144
struct {
void* pc;
int tid;
} pcdesc[BUF_SIZE];
static unsigned int ptr = 0;
void px(int tid) {
unsigned int p = ptr;
for (unsigned int i = 0; i < p; i++) {
if (pcdesc[i].tid == tid) {
const char* sym = ".";
Dl_info info;
if (dladdr(pcdesc[i].pc, &info) && info.dli_sname != NULL) {
sym = info.dli_sname;
}
printf("%5d %p %s\n", i, pcdesc[i].pc, sym);
}
}
}
static void noop_handler(int signo, siginfo_t* siginfo, void* ucontext) {
unsigned int p = __sync_fetch_and_add(&ptr, 1) & (BUF_SIZE - 1);
pcdesc[p].pc = (void*)((ucontext_t*)ucontext)->uc_mcontext.pc;
pcdesc[p].tid = syscall(__NR_gettid);
}
static void start_agent_thread() {
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = noop_handler;
sa.sa_flags = SA_SIGINFO | SA_RESTART;
sigaction(SIGPROF, &sa, NULL);
pthread_t thread;
if (pthread_create(&thread, NULL, signaling_loop, NULL) == 0) {
printf("signaling_loop started\n");
}
}
jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* reserved) {
start_agent_thread();
return 0;
}
jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved) {
start_agent_thread();
return 0;
}
#include <dirent.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>
#include <unistd.h>
static int is_compiler_thread(int tid) {
char buf[64];
snprintf(buf, sizeof(buf), "/proc/self/task/%d/comm", tid);
int fd = open(buf, O_RDONLY);
if (fd == -1) {
return 0;
}
ssize_t r = read(fd, buf, sizeof(buf));
close(fd);
return r > 0 && strncmp(buf, "C2 Compiler", 11) == 0;
}
static void* signaling_loop(void* unused) {
pid_t self = getpid();
unsigned int counter = 0;
DIR* dir = opendir("/proc/self/task");
if (dir == NULL) {
return NULL;
}
while (1) {
struct dirent* entry;
while ((entry = readdir(dir)) != NULL) {
if (entry->d_name[0] != '.') {
int tid = atoi(entry->d_name);
if (is_compiler_thread(tid)) {
syscall(__NR_tgkill, self, tid, SIGPROF);
}
}
}
rewinddir(dir);
}
}
static void noop_handler(int signo, siginfo_t* siginfo, void* ucontext) {
}
__attribute__((visibility("default")))
int Agent_OnLoad(void* vm, char* options, void* reserved) {
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = noop_handler;
sa.sa_flags = SA_SIGINFO | SA_RESTART;
sigaction(SIGPROF, &sa, NULL);
pthread_t thread;
if (pthread_create(&thread, NULL, signaling_loop, NULL) == 0) {
printf("Signaler started\n");
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment