This commit is contained in:
Academic 2026-07-20 12:30:11 +01:00
commit 5159f09114
15 changed files with 167460 additions and 0 deletions

63
.gitattributes vendored Normal file
View File

@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

55
.gitignore vendored Normal file
View File

@ -0,0 +1,55 @@
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
# debug information files
*.dwo

1
README.md Normal file
View File

@ -0,0 +1 @@
# GPUSuitabilityEst

View File

@ -0,0 +1,35 @@
APP := ebpf
BPF_SRC := ebpf.c
BPF_OBJ := $(APP).bpf.o
SKEL := $(APP).skel.h
VMLINUX := vmlinux.h
CLANG := clang
BPFOOL := bpftool
CFLAGS := -O2 -g -Wall
BPF_CFLAGS := -O2 -g -Wall -target bpf -D__TARGET_ARCH_x86 -I/usr/include
LIBS := -lbpf -lelf -lz
.PHONY: all run clean
all: $(APP)
$(VMLINUX):
$(BPFOOL) btf dump file /sys/kernel/btf/vmlinux format c > $@
$(BPF_OBJ): $(BPF_SRC) $(VMLINUX)
$(CLANG) $(BPF_CFLAGS) -c $(BPF_SRC) -o $@
$(SKEL): $(BPF_OBJ)
$(BPFOOL) gen skeleton $< > $@
$(APP): main.c container_worker.c data_structure/list_entry.c thread_mon/thread_mon.c $(SKEL)
$(CLANG) $(CFLAGS) main.c container_worker.c data_structure/list_entry.c thread_mon/thread_mon.c $(LIBS) -o $@
run: $(APP)
sudo ./$(APP)
clean:
rm -f $(APP) *.o *.skel.h vmlinux.h

View File

@ -0,0 +1,261 @@
#define _GNU_SOURCE
#include "container_worker.h"
static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
int cpu, int group_fd, unsigned long flags)
{
return syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags);
}
int open_perf_for_thread(thread_perf_list *t)
{
struct perf_event_attr pe[PERF_COUNT];
memset(pe, 0, sizeof(pe));
pe[PERF_TOTAL].type = PERF_TYPE_HARDWARE;
pe[PERF_TOTAL].size = sizeof(struct perf_event_attr);
pe[PERF_TOTAL].config = PERF_COUNT_HW_INSTRUCTIONS;
pe[PERF_SCALAR].type = PERF_TYPE_RAW;
pe[PERF_SCALAR].size = sizeof(struct perf_event_attr);
pe[PERF_SCALAR].config = 0x08 | (0x04 << 8);
pe[PERF_SCALAR_MAC].type = PERF_TYPE_RAW;
pe[PERF_SCALAR_MAC].size = sizeof(struct perf_event_attr);
pe[PERF_SCALAR_MAC].config = 0x0a | (0x04 << 8);
pe[PERF_PACK_128].type = PERF_TYPE_RAW;
pe[PERF_PACK_128].size = sizeof(struct perf_event_attr);
pe[PERF_PACK_128].config = 0x08 | (0x08 << 8);
pe[PERF_PACK_256].type = PERF_TYPE_RAW;
pe[PERF_PACK_256].size = sizeof(struct perf_event_attr);
pe[PERF_PACK_256].config = 0x08 | (0x10 << 8);
pe[PERF_PACK_512].type = PERF_TYPE_RAW;
pe[PERF_PACK_512].size = sizeof(struct perf_event_attr);
pe[PERF_PACK_512].config = 0x08 | (0x20 << 8);
pe[PERF_VECTOR_MAC].type = PERF_TYPE_RAW;
pe[PERF_VECTOR_MAC].size = sizeof(struct perf_event_attr);
pe[PERF_VECTOR_MAC].config = 0x0a | (0x40 << 8);
pe[PERF_INT_ALL].type = PERF_TYPE_RAW;
pe[PERF_INT_ALL].size = sizeof(struct perf_event_attr);
pe[PERF_INT_ALL].config = 0x0d | (0xff << 8);
pe[PERF_INT_128].type = PERF_TYPE_RAW;
pe[PERF_INT_128].size = sizeof(struct perf_event_attr);
pe[PERF_INT_128].config = 0x0d | (0x0f << 8);
pe[PERF_INT_256].type = PERF_TYPE_RAW;
pe[PERF_INT_256].size = sizeof(struct perf_event_attr);
pe[PERF_INT_256].config = 0x0d | (0xf0 << 8);
pe[PERF_EX_RET_BRN].type = PERF_TYPE_RAW;
pe[PERF_EX_RET_BRN].size = sizeof(struct perf_event_attr);
pe[PERF_EX_RET_BRN].config = 0xc2;
pe[PERF_EX_RET_BRN_MISP].type = PERF_TYPE_RAW;
pe[PERF_EX_RET_BRN_MISP].size = sizeof(struct perf_event_attr);
pe[PERF_EX_RET_BRN_MISP].config = 0xc3;
int failed = 0;
for (int i = 0; i < PERF_COUNT; i++)
{
t->fds[i] = perf_event_open(&pe[i], t->tid, -1, -1, 0);
if (t->fds[i] == -1)
failed = 1;
}
if (failed)
{
for (int i = 0; i < PERF_COUNT; i++)
if (t->fds[i] != -1)
close(t->fds[i]);
return -1;
}
for (int i = 0; i < PERF_COUNT; i++)
{
ioctl(t->fds[i], PERF_EVENT_IOC_RESET, 0);
ioctl(t->fds[i], PERF_EVENT_IOC_ENABLE, 0);
}
return 0;
}
// Free fds
void free_thread_perf(thread_perf_list *t)
{
for (int i = 0; i < PERF_COUNT; i++)
{
if (t->fds[i] != -1)
{
ioctl(t->fds[i], PERF_EVENT_IOC_DISABLE, 0);
close(t->fds[i]);
}
}
}
void print_perf_metrics(thread_perf_list *t, const char *cid,PCONTAINER_WORKER worker)
{
if (t->tid <= 0)
return;
uint64_t vals[PERF_COUNT];
memset(vals, 0, sizeof(vals));
uint64_t invalid = INVALID_COUNT;
for (int i = 0; i < PERF_COUNT; i++)
{
if (read(t->fds[i], &vals[i], sizeof(uint64_t)) == -1)
vals[i] = invalid;
}
time_t now = time(NULL);
char buf[64];
strftime(buf, sizeof(buf), "%Y%m%d%H%M%S", gmtime(&now));
/*
printf("[%s] TID %d:\n", buf, t->tid);
printf(" Total instructions: %lu\n", vals[PERF_TOTAL]);
printf(" Scalar FP instructions: %lu\n", vals[PERF_SCALAR]);
printf(" Scalar MAC/FMA: %lu\n", vals[PERF_SCALAR_MAC]);
printf(" 128-bit SIMD FP: %lu\n", vals[PERF_PACK_128]);
printf(" 256-bit SIMD FP: %lu\n", vals[PERF_PACK_256]);
printf(" 512-bit SIMD FP: %lu\n", vals[PERF_PACK_512]);
printf(" Vector MAC/FMA: %lu\n", vals[PERF_VECTOR_MAC]);
printf(" Packed Int (all): %lu\n", vals[PERF_INT_ALL]);
printf(" Packed Int 128-bit: %lu\n", vals[PERF_INT_128]);
printf(" Packed Int 256-bit: %lu\n", vals[PERF_INT_256]);
printf(" Branch Instructions: %lu\n", vals[PERF_EX_RET_BRN]);
printf(" Mispredicted Branch: %lu\n", vals[PERF_EX_RET_BRN_MISP]);
*/
for (int i = 0; i < PERF_COUNT; i++)
{
if (vals[i] != 0){
break;
}
return ;
}
printf("%s,%s,%d,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu\n",
buf, // 时间
cid, // 容器ID
t->tid, // TID
vals[PERF_TOTAL],
vals[PERF_SCALAR],
vals[PERF_SCALAR_MAC],
vals[PERF_PACK_128],
vals[PERF_PACK_256],
vals[PERF_PACK_512],
vals[PERF_VECTOR_MAC],
vals[PERF_INT_ALL],
vals[PERF_INT_128],
vals[PERF_INT_256],
vals[PERF_EX_RET_BRN],
vals[PERF_EX_RET_BRN_MISP],
worker->LibGPUCount,
worker->MMAPSize);
//if(worker->ContainerPid == t->tid)worker->MMAPSize=0;
}
void* delayed_free_perf(void* arg) {
thread_perf_list* t = (thread_perf_list*)arg;
usleep(EXIT_DELAY_MICROSECONDS);
RemoveEntryList(&t->entry);
free_thread_perf(t);
free(t);
return NULL;
}
void monitor_threads_dynamic(PCONTAINER_WORKER worker)
{
pid_t root_pid = worker->ContainerPid;
volatile int *IsRunning = &worker->IsRunning;
const char *cid = worker->ContainerId;
LIST_ENTRY thread_head;
InitializeListHead(&thread_head);
worker->threads = &thread_head;
while (*IsRunning)
{
pprocess_list plist = GetAllProcessAndThreadByPID(root_pid);
if (!plist)
continue;
PLIST_ENTRY iter = plist->entry.flink;
while (iter != &plist->entry)
{
pprocess_list node = CONTAINING_RECORD(iter, process_list, entry);
pid_t tid = node->pid;
if (tid <= 0)
{
iter = iter->flink;
continue;
}
thread_perf_list *pos;
int found = 0;
PLIST_ENTRY p = thread_head.flink;
while (p != &thread_head)
{
pos = CONTAINING_RECORD(p, thread_perf_list, entry);
if (pos->tid == tid)
{
found = 1;
break;
}
p = p->flink;
}
if (!found)
{
thread_perf_list *t = malloc(sizeof(*t));
memset(t, -1, sizeof(*t));
t->tid = tid;
if (open_perf_for_thread(t) == 0)
{
InsertTailList(&thread_head, &t->entry);
}
else
free(t);
}
iter = iter->flink;
}
FreeProcessOrThreadList(plist);
PLIST_ENTRY p = thread_head.flink;
while (p != &thread_head)
{
thread_perf_list *t = CONTAINING_RECORD(p, thread_perf_list, entry);
PLIST_ENTRY next = p->flink;
if (kill(t->tid, 0) == -1 && errno == ESRCH)
{
if(!t->Exited){
pthread_t tid;
pthread_create(&tid, NULL, delayed_free_perf, t);
pthread_detach(tid);
}
t->Exited = 1;
}
print_perf_metrics(t, cid,worker);
p = next;
}
usleep(CONTAINER_INFO_REFRESH_INTERVAL);
}
PLIST_ENTRY p = thread_head.flink;
while (p != &thread_head)
{
thread_perf_list *t = CONTAINING_RECORD(p, thread_perf_list, entry);
PLIST_ENTRY next = p->flink;
RemoveEntryList(&t->entry);
free_thread_perf(t);
free(t);
p = next;
}
}
void *ContainerWorkerEntry(void *wrk)
{
PCONTAINER_WORKER worker = (PCONTAINER_WORKER)wrk;
monitor_threads_dynamic(worker);
return NULL;
}

View File

@ -0,0 +1,67 @@
#pragma once
#include "thread_mon/thread_mon.h"
#include "data_structure/list_entry.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <signal.h>
#include <linux/perf_event.h>
#include <asm/unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <time.h>
#define MAX_CONTAINER_ID 128
#define PERF_TOTAL 0
#define PERF_SCALAR 1
#define PERF_SCALAR_MAC 2
#define PERF_PACK_128 3
#define PERF_PACK_256 4
#define PERF_PACK_512 5
#define PERF_VECTOR_MAC 6
#define PERF_INT_ALL 7
#define PERF_INT_128 8
#define PERF_INT_256 9
#define PERF_EX_RET_BRN 10
#define PERF_EX_RET_BRN_MISP 11
#define THREAD_MMAP_SIZE 12
#define PERF_COUNT 13
#define INVALID_COUNT 0xfa1dbabe
#define CONTAINER_INFO_REFRESH_INTERVAL 50000
#define EXIT_DELAY_MICROSECONDS 100000
typedef struct _CONTAINER_WORKER
{
LIST_ENTRY ListEntry;
char ContainerId[MAX_CONTAINER_ID];
pid_t ContainerPid;
volatile PLIST_ENTRY threads;
pthread_t MonitorThread;
volatile int IsRunning;
volatile uint64_t LibGPUCount;
volatile uint64_t MMAPSize;
volatile int Exited;
} CONTAINER_WORKER, *PCONTAINER_WORKER;
typedef struct _thread_perf_list
{
pid_t tid;
uint64_t fds[PERF_COUNT];
uint64_t fds_old[PERF_COUNT];
LIST_ENTRY entry;
volatile int Exited;
} thread_perf_list;
void *ContainerWorkerEntry(void *worker);

View File

@ -0,0 +1,45 @@
#include "list_entry.h"
void InitializeListHead(PLIST_ENTRY ListHead) {
ListHead->flink = ListHead;
ListHead->blink = ListHead;
}
int IsListEmpty(const PLIST_ENTRY ListHead) {
return ListHead->flink == ListHead;
}
void InsertHeadList(PLIST_ENTRY ListHead, PLIST_ENTRY Entry) {
Entry->flink = ListHead->flink;
Entry->blink = ListHead;
ListHead->flink->blink = Entry;
ListHead->flink = Entry;
}
void InsertTailList(PLIST_ENTRY ListHead, PLIST_ENTRY Entry) {
Entry->blink = ListHead->blink;
Entry->flink = ListHead;
ListHead->blink->flink = Entry;
ListHead->blink = Entry;
}
void RemoveEntryList(PLIST_ENTRY Entry) {
Entry->blink->flink = Entry->flink;
Entry->flink->blink = Entry->blink;
Entry->flink = Entry;
Entry->blink = Entry;
}
PLIST_ENTRY RemoveHeadList(PLIST_ENTRY ListHead) {
if (IsListEmpty(ListHead)) return NULL;
PLIST_ENTRY first = ListHead->flink;
RemoveEntryList(first);
return first;
}
PLIST_ENTRY RemoveTailList(PLIST_ENTRY ListHead) {
if (IsListEmpty(ListHead)) return NULL;
PLIST_ENTRY last = ListHead->blink;
RemoveEntryList(last);
return last;
}

View File

@ -0,0 +1,19 @@
#pragma once
typedef struct _LIST_ENTRY {
struct _LIST_ENTRY *flink;
struct _LIST_ENTRY *blink;
} LIST_ENTRY, *PLIST_ENTRY;
#include <stddef.h>
#define CONTAINING_RECORD(address, type, field) \
((type *)((char*)(address) - offsetof(type, field)))
void InitializeListHead(PLIST_ENTRY ListHead);
int IsListEmpty(const PLIST_ENTRY ListHead);
void InsertHeadList(PLIST_ENTRY ListHead, PLIST_ENTRY Entry);
void InsertTailList(PLIST_ENTRY ListHead, PLIST_ENTRY Entry);
void RemoveEntryList(PLIST_ENTRY Entry);
PLIST_ENTRY RemoveHeadList(PLIST_ENTRY ListHead);
PLIST_ENTRY RemoveTailList(PLIST_ENTRY ListHead);

122
code/kernel_module/ebpf.c Normal file
View File

@ -0,0 +1,122 @@
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
#include "event.h"
char LICENSE[] SEC("license") = "GPL";
struct
{
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 1 << 24);
} events SEC(".maps");
const char is_libcuda_needle[] = "libcuda";
static int is_libcuda(const char *p)
{
#pragma unroll
for (int i = 0; i < MAX_PATH - sizeof(is_libcuda_needle) - 1; i++)
{
int match = 1;
#pragma unroll
for (int j = 0; j < sizeof(is_libcuda_needle) - 1; j++)
{
if (p[i + j] != is_libcuda_needle[j])
{
match = 0;
break;
}
}
if (match)
return 1;
}
return 0;
}
const char is_libnv_needle[] = "libnv";
static int is_libnv(const char *p)
{
#pragma unroll
for (int i = 0; i < MAX_PATH - sizeof(is_libnv_needle) - 1; i++)
{
int match = 1;
#pragma unroll
for (int j = 0; j < sizeof(is_libnv_needle) - 1; j++)
{
if (p[i + j] != is_libnv_needle[j])
{
match = 0;
break;
}
}
if (match)
return 1;
}
return 0;
}
SEC("tracepoint/syscalls/sys_enter_openat")
int trace_openat(struct trace_event_raw_sys_enter *ctx)
{
const char *filename = (const char *)BPF_CORE_READ(ctx, args[1]);
char path[MAX_PATH];
if (bpf_probe_read_user_str(path, sizeof(path), filename) < 0)
return 0;
if (!is_libcuda(path) && !is_libnv(path))
return 0;
struct event *e = bpf_ringbuf_reserve(&events, sizeof(*e), 0);
if (!e)
return 0;
__builtin_memcpy(e->path, path, sizeof(path));
e->pid = bpf_get_current_pid_tgid() >> 32;
bpf_get_current_comm(&e->comm, sizeof(e->comm));
e->op = OPEN;
e->size = 0;
bpf_ringbuf_submit(e, 0);
return 0;
}
SEC("tracepoint/syscalls/sys_enter_mmap")
int trace_mmap(struct trace_event_raw_sys_enter *ctx)
{
struct event *e = bpf_ringbuf_reserve(&events, sizeof(*e), 0);
if (!e)
return 0;
e->pid = bpf_get_current_pid_tgid() >> 32;
bpf_get_current_comm(&e->comm, sizeof(e->comm));
e->op = MMAP;
e->size = BPF_CORE_READ(ctx, args[1]); // length
e->path[0] = 0;
bpf_ringbuf_submit(e, 0);
return 0;
}
SEC("tracepoint/syscalls/sys_enter_munmap")
int trace_munmap(struct trace_event_raw_sys_enter *ctx)
{
struct event *e = bpf_ringbuf_reserve(&events, sizeof(*e), 0);
if (!e)
return 0;
e->pid = bpf_get_current_pid_tgid() >> 32;
bpf_get_current_comm(&e->comm, sizeof(e->comm));
e->op = MUNMAP;
e->size = BPF_CORE_READ(ctx, args[1]); // length
e->path[0] = 0;
bpf_ringbuf_submit(e, 0);
return 0;
}

12182
code/kernel_module/ebpf.skel.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,22 @@
#ifndef EVENT_H
#define EVENT_H
#define MAX_PATH 256
#define COMM_LEN 16
typedef enum _OP{
INV = 0,
OPEN,
MMAP,
MUNMAP,
} EbpfOpCode;
struct event {
uint32_t pid;
char comm[COMM_LEN];
EbpfOpCode op;
char path[MAX_PATH];
uint64_t size;
};
#endif

341
code/kernel_module/main.c Normal file
View File

@ -0,0 +1,341 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <time.h>
#include <sys/resource.h>
#include <pthread.h>
#include <bpf/libbpf.h>
#include "ebpf.skel.h"
#include "event.h"
#include "data_structure/list_entry.h"
#include "container_worker.h"
#define MAX_CMD_LEN 512
LIST_ENTRY g_ContainerList;
PCONTAINER_WORKER
FindContainer(
const char *ContainerId)
{
if (IsListEmpty(&g_ContainerList))
return NULL;
PLIST_ENTRY Entry = g_ContainerList.flink;
while (Entry != &g_ContainerList)
{
PCONTAINER_WORKER Worker =
CONTAINING_RECORD(Entry, CONTAINER_WORKER, ListEntry);
if (strcmp(Worker->ContainerId, ContainerId) == 0)
return Worker;
Entry = Entry->flink;
}
return NULL;
}
void InsertContainer(
const char *ContainerId,
pid_t ContainerPid)
{
PCONTAINER_WORKER Worker =
(PCONTAINER_WORKER)malloc(sizeof(CONTAINER_WORKER));
if (!Worker)
return;
memset(Worker, 0, sizeof(CONTAINER_WORKER));
strncpy(Worker->ContainerId,
ContainerId,
MAX_CONTAINER_ID - 1);
Worker->ContainerPid = ContainerPid;
Worker->IsRunning = 1;
pthread_create(&Worker->MonitorThread, NULL,
ContainerWorkerEntry, Worker);
InsertTailList(&g_ContainerList, &Worker->ListEntry);
}
void RemoveContainer(
PCONTAINER_WORKER Worker)
{
Worker->IsRunning = 0;
pthread_join(Worker->MonitorThread, NULL);
RemoveEntryList(&Worker->ListEntry);
free(Worker);
}
void *delayed_free(void *arg)
{
PCONTAINER_WORKER Worker = (PCONTAINER_WORKER)arg;
usleep(EXIT_DELAY_MICROSECONDS);
RemoveContainer(Worker);
return NULL;
}
void RefreshContainerList(void)
{
FILE *Pipe = popen(
"docker inspect -f '{{.Name}} {{.State.Pid}}' $(docker ps -q) 2>/dev/null",
"r");
if (!Pipe)
{
perror("docker inspect failed");
return;
}
char Buffer[65536] = {0};
size_t Offset = 0;
while (fgets(Buffer + Offset, sizeof(Buffer) - Offset, Pipe))
{
Offset = strlen(Buffer); // 更新 Offset
if (Offset >= sizeof(Buffer) - 1)
break;
}
pclose(Pipe);
if (!IsListEmpty(&g_ContainerList))
{
PLIST_ENTRY Entry = g_ContainerList.flink;
while (Entry != &g_ContainerList)
{
PCONTAINER_WORKER Worker =
CONTAINING_RECORD(Entry, CONTAINER_WORKER, ListEntry);
Worker->ContainerPid = -1;
Entry = Entry->flink;
}
}
char *Line = strtok(Buffer, "\n");
while (Line)
{
char ContainerId[MAX_CONTAINER_ID];
int ContainerPid = -1;
if (sscanf(Line, "%64s %d", ContainerId, &ContainerPid) != 2)
{
Line = strtok(NULL, "\n");
continue;
}
if (ContainerId[0] == '/')
memmove(ContainerId, ContainerId + 1, strlen(ContainerId));
if (ContainerPid <= 0 || strncmp(ContainerId, "k8s", 3) == 0)
{
Line = strtok(NULL, "\n");
continue;
}
PCONTAINER_WORKER Worker = FindContainer(ContainerId);
if (Worker)
{
Worker->ContainerPid = ContainerPid;
}
else
{
InsertContainer(ContainerId, ContainerPid);
}
Line = strtok(NULL, "\n");
}
PLIST_ENTRY Entry = g_ContainerList.flink;
while (Entry != &g_ContainerList)
{
PCONTAINER_WORKER Worker =
CONTAINING_RECORD(Entry, CONTAINER_WORKER, ListEntry);
PLIST_ENTRY Next = Entry->flink;
if (Worker->ContainerPid == -1)
{
if(!Worker->Exited){
pthread_t tid;
pthread_create(&tid, NULL, delayed_free, Worker);
pthread_detach(tid);
}
//
Worker->Exited = 1;
}
Entry = Next;
}
}
static volatile sig_atomic_t exiting = 0;
static void handle_signal(int sig)
{
exiting = 1;
}
static int handle_event(void *ctx, void *data, size_t data_sz)
{
struct event *e = data;
pid_t pid = e->pid;
EbpfOpCode opcode = e->op;
if (IsListEmpty(&g_ContainerList))
{
return 0;
}
int print = 0;
PLIST_ENTRY Entry = g_ContainerList.flink;
while (Entry != &g_ContainerList)
{
PCONTAINER_WORKER Worker =
CONTAINING_RECORD(Entry, CONTAINER_WORKER, ListEntry);
do
{
if (pid == Worker->ContainerPid)
{
print = 1;
break;
}
if (Worker->threads == NULL)
break;
PLIST_ENTRY Threads = Worker->threads->flink;
while (Threads != Worker->threads)
{
thread_perf_list *Thread =
CONTAINING_RECORD(Threads, thread_perf_list, entry);
if (pid == Thread->tid)
{
print = 1;
break;
}
Threads = Threads->flink;
}
} while (0);
if (print)
{
if (opcode == OPEN)
Worker->LibGPUCount++;
if (opcode == MMAP && strcmp(e->comm,"runc:[2:INIT]"))
{
Worker->MMAPSize += (unsigned long long)e->size;
}
if (opcode == MUNMAP && strcmp(e->comm,"runc:[2:INIT]"))
{
Worker->MMAPSize -= (unsigned long long)e->size;
}
break;
}
Entry = Entry->flink;
}
// if (print && e->op == MUNMAP && strcmp(e->comm,"python"))
// {
// printf(
// "pid=%u comm=%s size=%llu path=%s\n",
// pid,
// e->comm,
// (unsigned long long)e->size,
// e->path[0] ? e->path : "-");
// }
return 0;
}
int main()
{
InitializeListHead(&g_ContainerList);
struct ebpf_bpf *skel;
struct ring_buffer *rb = NULL;
int err;
struct rlimit rlim = {
.rlim_cur = RLIM_INFINITY,
.rlim_max = RLIM_INFINITY,
};
setrlimit(RLIMIT_MEMLOCK, &rlim);
signal(SIGINT, handle_signal);
signal(SIGTERM, handle_signal);
skel = ebpf_bpf__open();
if (!skel)
{
fprintf(stderr, "Failed to open BPF skeleton\n");
return 1;
}
do
{
err = ebpf_bpf__load(skel);
if (err)
{
fprintf(stderr, "Failed to load BPF skeleton: %d\n", err);
break;
}
err = ebpf_bpf__attach(skel);
if (err)
{
fprintf(stderr, "Failed to attach BPF programs: %d\n", err);
break;
}
rb = ring_buffer__new(
bpf_map__fd(skel->maps.events),
handle_event,
NULL,
NULL);
if (!rb)
{
fprintf(stderr, "Failed to create ring buffer\n");
break;
}
setvbuf(stdout, NULL, _IONBF, 0);
printf("time,cid,tid,total,scalar,scalar_mac,pack_128,pack_256,pack_512,vector_mac,int_all,int_128,int_256,ex_ret_brn,ex_ret_brn_misp,libgpuopen,mmapsize\n");
while (!exiting)
{
RefreshContainerList();
err = ring_buffer__poll(rb, 10);
if (err == -EINTR)
break;
if (err < 0)
{
fprintf(stderr, "ring_buffer__poll error: %d\n", err);
break;
}
usleep(1000000);
}
} while (0);
ring_buffer__free(rb);
ebpf_bpf__destroy(skel);
return err < 0 ? -err : 0;
return 0;
}

View File

@ -0,0 +1,144 @@
#include "thread_mon.h"
int is_number(const char *str) {
while (*str) {
if (!isdigit(*str)) return 0;
str++;
}
return 1;
}
pprocess_list GetProcessListByPID(int pid) {
pprocess_list head = malloc(sizeof(process_list));
if (!head) return NULL;
head->pid = -1;
InitializeListHead(&head->entry);
PLIST_ENTRY list_head = &head->entry;
DIR *dir = opendir("/proc");
if (!dir) return head;
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
if (!is_number(entry->d_name)) continue;
int cpid = atoi(entry->d_name);
if (cpid <= 0) continue;
char stat_path[256];
snprintf(stat_path, sizeof(stat_path), "/proc/%d/stat", cpid);
FILE *fp = fopen(stat_path, "r");
if (!fp) continue;
int ppid;
char name[256];
if (fscanf(fp, "%*d (%[^)]) %*c %d", name, &ppid) != 2) {
fclose(fp);
continue;
}
fclose(fp);
if (ppid == pid) {
pprocess_list node = malloc(sizeof(process_list));
if (!node) continue;
node->pid = cpid;
InitializeListHead(&node->entry);
InsertTailList(list_head, &node->entry);
pprocess_list child_list = GetProcessListByPID(cpid);
if (child_list) {
PLIST_ENTRY iter = child_list->entry.flink;
while (iter != &child_list->entry) {
pprocess_list child_node = CONTAINING_RECORD(iter, process_list, entry);
iter = iter->flink;
InsertTailList(list_head, &child_node->entry);
}
free(child_list);child_list=NULL;
}
}
}
closedir(dir);
return head;
}
pprocess_list GetThreadListByPID(int pid){
char path[256];
snprintf(path, sizeof(path), "/proc/%d/task", pid);
DIR *dir = opendir(path);
if (!dir) return NULL;
pprocess_list fa = malloc(sizeof(process_list));
if (!fa) { return NULL; }
InitializeListHead(&fa->entry);
fa->pid = -1;
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
char *name = entry->d_name;
if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
continue;
if (!is_number(name))
continue;
int tid = atoi(name);
if (tid <= 0) continue;
pprocess_list node = malloc(sizeof(process_list));
if (!node) continue;
node->pid = tid;
InsertTailList(&fa->entry, &node->entry);
}
closedir(dir);
return fa;
}
pprocess_list GetAllProcessAndThreadByPID(int pid) {
pprocess_list head = malloc(sizeof(process_list));
if (!head) return NULL;
head->pid = -1;
InitializeListHead(&head->entry);
PLIST_ENTRY list_head = &head->entry;
pprocess_list thread_list = GetThreadListByPID(pid);
if (thread_list) {
PLIST_ENTRY iter = thread_list->entry.flink;
while (iter != &thread_list->entry) {
pprocess_list node = CONTAINING_RECORD(iter, process_list, entry);
iter = iter->flink;
InsertTailList(list_head, &node->entry);
}
free(thread_list);thread_list=NULL;
}
pprocess_list child_list = GetProcessListByPID(pid);
if (child_list) {
PLIST_ENTRY iter = child_list->entry.flink;
while (iter != &child_list->entry) {
pprocess_list node = CONTAINING_RECORD(iter, process_list, entry);
iter = iter->flink;
//InsertTailList(list_head, &node->entry);
pprocess_list child_thread = GetThreadListByPID(node->pid);
if (child_thread) {
PLIST_ENTRY titer = child_thread->entry.flink;
while (titer != &child_thread->entry) {
pprocess_list tnode = CONTAINING_RECORD(titer, process_list, entry);
titer = titer->flink;
InsertTailList(list_head, &tnode->entry);
}
free(child_thread);child_thread=NULL;
}
}
FreeProcessOrThreadList(child_list);
}
return head;
}
void FreeProcessOrThreadList(pprocess_list list){
if (list == NULL) return ;
while (!IsListEmpty(&list->entry)) {
PLIST_ENTRY removed = RemoveHeadList(&list->entry);
free(CONTAINING_RECORD(removed, process_list, entry));
}
free(list);
list = NULL;
}

View File

@ -0,0 +1,49 @@
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <ctype.h>
#include <unistd.h>
#include <string.h>
#include "../data_structure/list_entry.h"
#define THREAD_MON_PROC_DIR "/proc"
#define MAX_PROC 32768
#define MAX_NAME 256
#define MAX_ARRAY 65536
typedef struct _process_list {
LIST_ENTRY entry;
int pid;
} process_list, *pprocess_list;
/**
* Get all child processes of the given PID.
* Returns a flat linked list (pprocess_list).
* The head node's pid is -1, used only as the list entry.
* Memory allocated for nodes must be freed using FreeProcessOrThreadList().
*/
pprocess_list GetProcessListByPID(int pid);
/**
* Get all threads (tasks) of the given PID.
* Returns a flat linked list.
* The head node's pid is -1, used only as the list entry.
* Memory allocated for nodes must be freed using FreeProcessOrThreadList().
*/
pprocess_list GetThreadListByPID(int pid);
/**
* Get all threads and child processes of the given PID recursively.
* Returns a flat linked list.
* The head node's pid is -1, used only as the list entry.
* Memory allocated for nodes must be freed using FreeProcessOrThreadList().
*/
pprocess_list GetAllProcessAndThreadByPID(int pid);
/**
* Free the linked list returned by the above functions, including all nodes.
*/
void FreeProcessOrThreadList(pprocess_list list);

154054
code/kernel_module/vmlinux.h Normal file

File diff suppressed because it is too large Load Diff