Skip to content

Commit 34dffa8

Browse files
committed
self-profile: add LD_PRELOAD Support
This code is a simple implementation of my idea, with a focus on making the self-profile portable. It seems useful, even if there's a call to the main function, because This method seems to reduce overhead compared to using "perf record" directly. We can directly insert the code according to its original purpose. Here are the test results from my Raspberry Pi 5, gcc version 12.2.0 (Debian 12.2.0-14) $ uname -a Linux paran 6.10.1-v8-16k+ ThinkOpenly#1 SMP PREEMPT Sat Jul 27 17:52:03 KST 2024 aarch64 GNU/Linux $ make run export PERF_COUNT_HW_CPU_CYCLES=1; ./test_profile Sorting... 00: { "H", 107, 0.900000 } 01: { "I", 111, 0.900000 } 02: { "G", 117, 0.900000 } 03: { "E", 127, 0.900000 } 04: { "F", 147, 0.900000 } 05: { "A", 157, 0.900000 } 06: { "K", 157, 0.900000 } 07: { "L", 157, 0.900000 } 08: { "M", 157, 0.900000 } 09: { "N", 157, 0.900000 } 10: { "O", 157, 0.900000 } 11: { "P", 157, 0.900000 } 12: { "Z", 157, 0.900000 } 13: { "C", 175, 0.900000 } 14: { "J", 227, 0.900000 } 15: { "B", 517, 0.900000 } 16: { "D", 571, 0.900000 } PERF_COUNT_HW_CPU_CYCLES(0): 7970 export PERF_COUNT_HW_CPU_CYCLES=1; LD_PRELOAD=self-profile.so ./preload_test_profile Sorting... PERF_COUNT_HW_CPU_CYCLES(0): 7444 export PERF_COUNT_HW_CPU_CYCLES=1; LD_PRELOAD=self-profile.so ./bsearch Sorting... PERF_COUNT_HW_CPU_CYCLES(0): 6779 Signed-off-by: Yunseong Kim <[email protected]>
1 parent 2741d08 commit 34dffa8

File tree

5 files changed

+153
-1
lines changed

5 files changed

+153
-1
lines changed

Makefile

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
11
CFLAGS=-O -Wno-unused-result
2+
3+
all: test_profile self-profile preload_test_profile bsearch
4+
25
test_profile: test_profile.o
36

7+
self-profile:
8+
$(CC) self-profile.c -o self-profile.so -fPIC -shared -ldl
9+
10+
preload_test_profile: preload_test_profile.o
11+
12+
bsearch: bsearch.o
13+
414
.PHONY: clean
515
clean:
6-
rm test_profile test_profile.o
16+
rm test_profile test_profile.o self-profile.so preload_test_profile.o preload_test_profile bsearch.o bsearch
717

818
.PHONY: run
919
run: test_profile
1020
export PERF_COUNT_HW_CPU_CYCLES=1; ./test_profile
21+
export PERF_COUNT_HW_CPU_CYCLES=1; LD_PRELOAD=self-profile.so ./preload_test_profile
22+
export PERF_COUNT_HW_CPU_CYCLES=1; LD_PRELOAD=self-profile.so ./bsearch

bsearch.c

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Reference:
3+
* - https://man7.org/linux/man-pages/man3/bsearch.3.html
4+
*/
5+
6+
#include <stdio.h>
7+
#include <stdlib.h>
8+
#include <string.h>
9+
struct mi {
10+
int nr;
11+
char *name;
12+
} months[] = {
13+
{ 1, "jan" }, { 2, "feb" }, { 3, "mar" }, { 4, "apr" },
14+
{ 5, "may" }, { 6, "jun" }, { 7, "jul" }, { 8, "aug" },
15+
{ 9, "sep" }, {10, "oct" }, {11, "nov" }, {12, "dec" }
16+
};
17+
#define nr_of_months (sizeof(months)/sizeof(months[0]))
18+
static int
19+
compmi(const void *m1, const void *m2)
20+
{
21+
struct mi *mi1 = (struct mi *) m1;
22+
struct mi *mi2 = (struct mi *) m2;
23+
return strcmp(mi1->name, mi2->name);
24+
}
25+
int
26+
main(int argc, char **argv)
27+
{
28+
int i;
29+
qsort(months, nr_of_months, sizeof(struct mi), compmi);
30+
for (i = 1; i < argc; i++) {
31+
struct mi key, *res;
32+
key.name = argv[i];
33+
res = bsearch(&key, months, nr_of_months,
34+
sizeof(struct mi), compmi);
35+
if (res == NULL)
36+
return 1;
37+
}
38+
return 0;
39+
}

preload_test_profile.c

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#define _GNU_SOURCE
2+
#include <stdlib.h>
3+
4+
/*
5+
void qsort_r(void *base, size_t nmemb, size_t size,
6+
int (*compar)(const void *, const void *, void *),
7+
void *arg);
8+
*/
9+
10+
struct item {
11+
char *name;
12+
int id;
13+
double rating;
14+
} items[] = {
15+
{ "A", 157, 0.9 },
16+
{ "B", 517, 0.9 },
17+
{ "C", 175, 0.9 },
18+
{ "D", 571, 0.9 },
19+
{ "E", 127, 0.9 },
20+
{ "F", 147, 0.9 },
21+
{ "G", 117, 0.9 },
22+
{ "H", 107, 0.9 },
23+
{ "I", 111, 0.9 },
24+
{ "J", 227, 0.9 },
25+
{ "K", 157, 0.9 },
26+
{ "L", 157, 0.9 },
27+
{ "M", 157, 0.9 },
28+
{ "N", 157, 0.9 },
29+
{ "O", 157, 0.9 },
30+
{ "P", 157, 0.9 },
31+
{ "Z", 157, 0.9 }
32+
};
33+
34+
int compare_items(const void *a, const void *b, void *arg) {
35+
const struct item *itemL = a, *itemR = b;
36+
if (itemL->id < itemR->id) return -1;
37+
if (itemL->id > itemR->id) return 1;
38+
return 0;
39+
}
40+
41+
int main() {
42+
qsort_r(items, sizeof(items)/sizeof(items[0]), sizeof(items[0]), compare_items, 0);
43+
return 0;
44+
}

self-profile.c

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#define _GNU_SOURCE
2+
#include "self-profile.h"
3+
4+
/*
5+
* Reference:
6+
* Hook main() using LD_PRELOAD:
7+
* - https://gist.github.com/apsun/1e144bf7639b22ff0097171fa0f8c6b1
8+
* Using makefile, LD_PRELOAD to executable file:
9+
* - https://stackoverflow.com/questions/30594838/using-makefile-ld-preload-to-executable-file
10+
*/
11+
12+
/* Trampoline for the real main() */
13+
static int (*main_orig)(int, char **, char **);
14+
15+
/* main() that gets called by __libc_start_main() */
16+
int main_hook(int argc, char **argv, char **envp)
17+
{
18+
PROFILE_BEGIN();
19+
printf("Sorting...\n");
20+
PROFILE_START();
21+
int ret = main_orig(argc, argv, envp);
22+
PROFILE_STOP();
23+
/*
24+
* I'm a bit concerned about this part of the code.
25+
* It would be great if we could use the output section universally like main function.
26+
* I'll think about it a bit more.
27+
*/
28+
PROFILE_REPORT();
29+
PROFILE_END();
30+
31+
return ret;
32+
}
33+
34+
/*
35+
* Wrapper for __libc_start_main() that replaces the real main
36+
* function with our hooked version.
37+
*/
38+
int __libc_start_main(
39+
int (*main)(int, char **, char **),
40+
int argc,
41+
char **argv,
42+
int (*init)(int, char **, char **),
43+
void (*fini)(void),
44+
void (*rtld_fini)(void),
45+
void *stack_end)
46+
{
47+
/* Save the real main function address */
48+
main_orig = main;
49+
50+
/* Find the real __libc_start_main()... */
51+
typeof(&__libc_start_main) orig = dlsym(RTLD_NEXT, "__libc_start_main");
52+
53+
/* ... and call it with our custom main function */
54+
return orig(main_hook, argc, argv, init, fini, rtld_fini, stack_end);
55+
}
56+

self-profile.h

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <stdio.h>
66
#include <string.h>
77
#include <unistd.h>
8+
#include <dlfcn.h>
89
#include <sys/ioctl.h>
910
#include <linux/perf_event.h>
1011
#include <asm/unistd.h>

0 commit comments

Comments
 (0)