init
This commit is contained in:
commit
5159f09114
63
.gitattributes
vendored
Normal file
63
.gitattributes
vendored
Normal 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
55
.gitignore
vendored
Normal 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
|
||||
35
code/kernel_module/Makefile
Normal file
35
code/kernel_module/Makefile
Normal 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
|
||||
261
code/kernel_module/container_worker.c
Normal file
261
code/kernel_module/container_worker.c
Normal 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;
|
||||
}
|
||||
67
code/kernel_module/container_worker.h
Normal file
67
code/kernel_module/container_worker.h
Normal 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);
|
||||
45
code/kernel_module/data_structure/list_entry.c
Normal file
45
code/kernel_module/data_structure/list_entry.c
Normal 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;
|
||||
}
|
||||
19
code/kernel_module/data_structure/list_entry.h
Normal file
19
code/kernel_module/data_structure/list_entry.h
Normal 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
122
code/kernel_module/ebpf.c
Normal 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
12182
code/kernel_module/ebpf.skel.h
Normal file
File diff suppressed because it is too large
Load Diff
22
code/kernel_module/event.h
Normal file
22
code/kernel_module/event.h
Normal 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
341
code/kernel_module/main.c
Normal 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;
|
||||
}
|
||||
144
code/kernel_module/thread_mon/thread_mon.c
Normal file
144
code/kernel_module/thread_mon/thread_mon.c
Normal 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;
|
||||
}
|
||||
49
code/kernel_module/thread_mon/thread_mon.h
Normal file
49
code/kernel_module/thread_mon/thread_mon.h
Normal 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
154054
code/kernel_module/vmlinux.h
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user