Skip to content

Commit 3f2f71a

Browse files
committed
feat: add i/o native interposition
1 parent b86395b commit 3f2f71a

24 files changed

Lines changed: 2195 additions & 14 deletions

ddprof-lib/src/main/cpp/codeCache.cpp

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,13 +310,21 @@ void CodeCache::saveImport(ImportId id, void** entry) {
310310
void CodeCache::addImport(void **entry, const char *name) {
311311
switch (name[0]) {
312312
case 'a':
313-
if (strcmp(name, "aligned_alloc") == 0) {
313+
if (strcmp(name, "accept") == 0) {
314+
saveImport(im_accept, entry);
315+
} else if (strcmp(name, "accept4") == 0) {
316+
saveImport(im_accept4, entry);
317+
} else if (strcmp(name, "aligned_alloc") == 0) {
314318
saveImport(im_aligned_alloc, entry);
315319
}
316320
break;
317321
case 'c':
318322
if (strcmp(name, "calloc") == 0) {
319323
saveImport(im_calloc, entry);
324+
} else if (strcmp(name, "close") == 0) {
325+
saveImport(im_close, entry);
326+
} else if (strcmp(name, "connect") == 0) {
327+
saveImport(im_connect, entry);
320328
}
321329
break;
322330
case 'd':
@@ -329,6 +337,13 @@ void CodeCache::addImport(void **entry, const char *name) {
329337
saveImport(im_free, entry);
330338
}
331339
break;
340+
case 'e':
341+
if (strcmp(name, "epoll_wait") == 0) {
342+
saveImport(im_epoll_wait, entry);
343+
} else if (strcmp(name, "epoll_pwait") == 0) {
344+
saveImport(im_epoll_pwait, entry);
345+
}
346+
break;
332347
case 'm':
333348
if (strcmp(name, "malloc") == 0) {
334349
saveImport(im_malloc, entry);
@@ -343,18 +358,39 @@ void CodeCache::addImport(void **entry, const char *name) {
343358
saveImport(im_pthread_setspecific, entry);
344359
} else if (strcmp(name, "poll") == 0) {
345360
saveImport(im_poll, entry);
361+
} else if (strcmp(name, "ppoll") == 0) {
362+
saveImport(im_ppoll, entry);
363+
} else if (strcmp(name, "pselect") == 0) {
364+
saveImport(im_pselect, entry);
346365
} else if (strcmp(name, "posix_memalign") == 0) {
347366
saveImport(im_posix_memalign, entry);
348367
}
349368
break;
350369
case 'r':
351370
if (strcmp(name, "realloc") == 0) {
352371
saveImport(im_realloc, entry);
372+
} else if (strcmp(name, "recv") == 0) {
373+
saveImport(im_recv, entry);
374+
} else if (strcmp(name, "recvfrom") == 0) {
375+
saveImport(im_recvfrom, entry);
376+
} else if (strcmp(name, "recvmsg") == 0) {
377+
saveImport(im_recvmsg, entry);
378+
} else if (strcmp(name, "read") == 0) {
379+
saveImport(im_read, entry);
353380
}
354381
break;
355382
case 's':
356383
if (strcmp(name, "sigaction") == 0) {
357384
saveImport(im_sigaction, entry);
385+
} else if (strcmp(name, "send") == 0) {
386+
saveImport(im_send, entry);
387+
} else if (strcmp(name, "select") == 0) {
388+
saveImport(im_select, entry);
389+
}
390+
break;
391+
case 'w':
392+
if (strcmp(name, "write") == 0) {
393+
saveImport(im_write, entry);
358394
}
359395
break;
360396
}
@@ -456,4 +492,3 @@ void CodeCache::setBuildId(const char* build_id, size_t build_id_len) {
456492
}
457493
}
458494
}
459-

ddprof-lib/src/main/cpp/codeCache.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,21 @@ enum ImportId {
3838
im_posix_memalign,
3939
im_aligned_alloc,
4040
im_sigaction,
41+
im_send,
42+
im_recv,
43+
im_write,
44+
im_read,
45+
im_close,
46+
im_connect,
47+
im_accept,
48+
im_accept4,
49+
im_recvfrom,
50+
im_recvmsg,
51+
im_epoll_wait,
52+
im_epoll_pwait,
53+
im_ppoll,
54+
im_select,
55+
im_pselect,
4156
NUM_IMPORTS
4257
};
4358

ddprof-lib/src/main/cpp/flightRecorder.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1408,7 +1408,7 @@ void Recording::writeFrameTypes(Buffer *buf) {
14081408

14091409
void Recording::writeThreadStates(Buffer *buf) {
14101410
buf->putVar64(T_THREAD_STATE);
1411-
buf->put8(10);
1411+
buf->put8(11);
14121412
buf->put8(static_cast<int>(OSThreadState::UNKNOWN));
14131413
buf->putUtf8("UNKNOWN");
14141414
buf->put8(static_cast<int>(OSThreadState::NEW));
@@ -1429,6 +1429,8 @@ void Recording::writeThreadStates(Buffer *buf) {
14291429
buf->putUtf8("TERMINATED");
14301430
buf->put8(static_cast<int>(OSThreadState::SYSCALL));
14311431
buf->putUtf8("SYSCALL");
1432+
buf->put8(static_cast<int>(OSThreadState::IO_WAIT));
1433+
buf->putUtf8("IO_WAIT");
14321434
flushIfNeeded(buf);
14331435
}
14341436

ddprof-lib/src/main/cpp/javaApi.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,7 @@ static OSThreadState decodeTaskBlockObservedState(jint state) {
376376
case OSThreadState::OBJECT_WAIT:
377377
case OSThreadState::SLEEPING:
378378
case OSThreadState::SYSCALL:
379+
case OSThreadState::IO_WAIT:
379380
return static_cast<OSThreadState>(state);
380381
default:
381382
return OSThreadState::UNKNOWN;

ddprof-lib/src/main/cpp/libraries.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ void Libraries::refresh() {
6060
_dirty.store(false, std::memory_order_release);
6161
updateSymbols(false);
6262
LibraryPatcher::patch_sigaction();
63+
LibraryPatcher::install_socket_hooks();
6364
MallocTracer::installHooks();
6465
if (_remote_symbolication) {
6566
updateBuildIds();

ddprof-lib/src/main/cpp/libraryPatcher.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#include "codeCache.h"
55
#include "spinLock.h"
66

7+
#include <atomic>
8+
79
#ifdef __linux__
810

911
// Patch libraries' @plt entries
@@ -15,6 +17,7 @@ typedef struct _patchEntry {
1517
void* _func;
1618
} PatchEntry;
1719

20+
const int MAX_NATIVE_IO_HOOKS = 16;
1821

1922
class LibraryPatcher {
2023
private:
@@ -28,15 +31,28 @@ class LibraryPatcher {
2831
static PatchEntry _sigaction_entries[MAX_NATIVE_LIBS];
2932
static int _sigaction_size;
3033

34+
// Separate tracking for native I/O patches. Each library can contribute one
35+
// GOT slot per supported I/O hook.
36+
static PatchEntry _socket_entries[MAX_NATIVE_IO_HOOKS * MAX_NATIVE_LIBS];
37+
static int _socket_size;
38+
3139
static void patch_library_unlocked(CodeCache* lib);
3240
static void patch_pthread_create();
3341
static void patch_pthread_setspecific();
3442
static void patch_sigaction_in_library(CodeCache* lib);
3543
public:
44+
static std::atomic<bool> _socket_active;
3645
static void initialize();
3746
static void patch_libraries();
3847
static void unpatch_libraries();
3948
static void patch_sigaction();
49+
static bool patch_socket_functions();
50+
static void unpatch_socket_functions();
51+
static inline void install_socket_hooks() {
52+
if (_socket_active.load(std::memory_order_acquire)) {
53+
patch_socket_functions();
54+
}
55+
}
4056
};
4157

4258
#else
@@ -47,8 +63,11 @@ class LibraryPatcher {
4763
static void patch_libraries() { }
4864
static void unpatch_libraries() { }
4965
static void patch_sigaction() { }
66+
static bool patch_socket_functions() { return false; }
67+
static void unpatch_socket_functions() { }
68+
static void install_socket_hooks() { }
5069
};
5170

5271
#endif
5372

54-
#endif // _LIBRARYPATCHER_H
73+
#endif // _LIBRARYPATCHER_H

ddprof-lib/src/main/cpp/libraryPatcher_linux.cpp

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "counters.h"
1010
#include "profiler.h"
1111
#include "guards.h"
12+
#include "nativeSocketInterposer.h"
1213

1314
#include <cassert>
1415
#include <dlfcn.h>
@@ -24,6 +25,9 @@ PatchEntry LibraryPatcher::_patched_entries[MAX_NATIVE_LIBS];
2425
int LibraryPatcher::_size = 0;
2526
PatchEntry LibraryPatcher::_sigaction_entries[MAX_NATIVE_LIBS];
2627
int LibraryPatcher::_sigaction_size = 0;
28+
PatchEntry LibraryPatcher::_socket_entries[MAX_NATIVE_IO_HOOKS * MAX_NATIVE_LIBS];
29+
int LibraryPatcher::_socket_size = 0;
30+
std::atomic<bool> LibraryPatcher::_socket_active{false};
2731

2832
void LibraryPatcher::initialize() {
2933
if (_profiler_name == nullptr) {
@@ -382,4 +386,114 @@ void LibraryPatcher::patch_sigaction() {
382386
}
383387
}
384388

389+
bool LibraryPatcher::patch_socket_functions() {
390+
static void* cached_originals[NativeSocketInterposer::NUM_NATIVE_IO_HOOKS] = {};
391+
static bool cached = false;
392+
393+
const NativeSocketInterposer::NativeIoHookSpec* hooks =
394+
NativeSocketInterposer::hookSpecs();
395+
396+
if (!cached) {
397+
for (int hook_index = 0; hook_index < NativeSocketInterposer::NUM_NATIVE_IO_HOOKS;
398+
hook_index++) {
399+
void* original = dlsym(RTLD_NEXT, hooks[hook_index].name);
400+
if (original == nullptr) {
401+
original = dlsym(RTLD_DEFAULT, hooks[hook_index].name);
402+
}
403+
if (original == hooks[hook_index].hook) {
404+
original = nullptr;
405+
}
406+
cached_originals[hook_index] = original;
407+
}
408+
cached = true;
409+
}
410+
411+
bool any_original = false;
412+
for (int hook_index = 0; hook_index < NativeSocketInterposer::NUM_NATIVE_IO_HOOKS;
413+
hook_index++) {
414+
any_original |= cached_originals[hook_index] != nullptr;
415+
}
416+
if (!any_original) {
417+
return false;
418+
}
419+
420+
const CodeCacheArray& native_libs = Libraries::instance()->native_libs();
421+
int num_of_libs = native_libs.count();
422+
int capped = num_of_libs <= MAX_NATIVE_LIBS ? num_of_libs : MAX_NATIVE_LIBS;
423+
bool is_self[MAX_NATIVE_LIBS];
424+
for (int index = 0; index < capped; index++) {
425+
CodeCache* lib = native_libs.at(index);
426+
is_self[index] = false;
427+
if (lib == nullptr || lib->name() == nullptr) {
428+
continue;
429+
}
430+
char path[PATH_MAX];
431+
char* resolved_path = realpath(lib->name(), path);
432+
is_self[index] = _profiler_name != nullptr && resolved_path != nullptr &&
433+
strcmp(resolved_path, _profiler_name) == 0;
434+
}
435+
436+
ExclusiveLockGuard locker(&_lock);
437+
if (_socket_size == 0) {
438+
for (int hook_index = 0; hook_index < NativeSocketInterposer::NUM_NATIVE_IO_HOOKS;
439+
hook_index++) {
440+
if (cached_originals[hook_index] != nullptr) {
441+
NativeSocketInterposer::setOriginalFunction(hook_index,
442+
cached_originals[hook_index]);
443+
}
444+
}
445+
}
446+
447+
auto try_patch_slot = [](void** location, void* hook, const char* name,
448+
CodeCache* lib) {
449+
if (location == nullptr) {
450+
return;
451+
}
452+
for (int index = 0; index < _socket_size; index++) {
453+
if (_socket_entries[index]._location == location) {
454+
return;
455+
}
456+
}
457+
if (_socket_size >= MAX_NATIVE_IO_HOOKS * MAX_NATIVE_LIBS) {
458+
Log::warn("socket I/O patch table full, skipping %s in %s", name,
459+
lib != nullptr ? lib->name() : "?");
460+
return;
461+
}
462+
_socket_entries[_socket_size]._lib = lib;
463+
_socket_entries[_socket_size]._location = location;
464+
_socket_entries[_socket_size]._func =
465+
reinterpret_cast<void*>(__atomic_load_n(location, __ATOMIC_ACQUIRE));
466+
__atomic_store_n(location, hook, __ATOMIC_RELEASE);
467+
_socket_size++;
468+
};
469+
470+
for (int index = 0; index < capped; index++) {
471+
CodeCache* lib = native_libs.at(index);
472+
if (lib == nullptr || lib->name() == nullptr || is_self[index]) {
473+
continue;
474+
}
475+
for (int hook_index = 0; hook_index < NativeSocketInterposer::NUM_NATIVE_IO_HOOKS;
476+
hook_index++) {
477+
if (cached_originals[hook_index] == nullptr) {
478+
continue;
479+
}
480+
try_patch_slot(reinterpret_cast<void**>(lib->findImport(hooks[hook_index].import_id)),
481+
hooks[hook_index].hook, hooks[hook_index].name, lib);
482+
}
483+
}
484+
485+
_socket_active.store(true, std::memory_order_release);
486+
return true;
487+
}
488+
489+
void LibraryPatcher::unpatch_socket_functions() {
490+
ExclusiveLockGuard locker(&_lock);
491+
for (int index = 0; index < _socket_size; index++) {
492+
__atomic_store_n(_socket_entries[index]._location, _socket_entries[index]._func,
493+
__ATOMIC_RELEASE);
494+
}
495+
_socket_active.store(false, std::memory_order_release);
496+
_socket_size = 0;
497+
}
498+
385499
#endif // __linux__

0 commit comments

Comments
 (0)