From 7f18b352b0fc010efe5278ce32e01ed715ddd7ee Mon Sep 17 00:00:00 2001 From: Fynn Wulf Date: Fri, 9 Oct 2020 15:13:06 +0200 Subject: [PATCH] Calculate library size (M_LRS column) from maps file --- Process.h | 4 ++- Settings.c | 2 +- Settings.h | 2 +- linux/LinuxProcess.c | 2 +- linux/LinuxProcess.h | 17 ++++----- linux/LinuxProcessList.c | 76 ++++++++++++++++++++++++++++++++++++++-- 6 files changed, 89 insertions(+), 14 deletions(-) diff --git a/Process.h b/Process.h index 774d337f..2fb27968 100644 --- a/Process.h +++ b/Process.h @@ -9,6 +9,8 @@ in the source distribution for its full text. */ #include +#include +#include #include #include "Object.h" @@ -113,7 +115,7 @@ typedef struct ProcessFieldData_ { const char* name; const char* title; const char* description; - int flags; + uint32_t flags; } ProcessFieldData; // Implemented in platform-specific code: diff --git a/Settings.c b/Settings.c index 1daea7dc..f2019e9a 100644 --- a/Settings.c +++ b/Settings.c @@ -90,7 +90,7 @@ static void Settings_defaultMeters(Settings* this, int initialCpuCount) { this->columns[1].modes[r++] = TEXT_METERMODE; } -static void readFields(ProcessField* fields, int* flags, const char* line) { +static void readFields(ProcessField* fields, uint32_t* flags, const char* line) { char* trim = String_trim(line); char** ids = String_split(trim, ' ', NULL); free(trim); diff --git a/Settings.h b/Settings.h index 3c829ee1..b01ce23f 100644 --- a/Settings.h +++ b/Settings.h @@ -27,7 +27,7 @@ typedef struct Settings_ { MeterColumnSettings columns[2]; ProcessField* fields; - int flags; + uint32_t flags; int colorScheme; int delay; diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index 93d169e3..1dd30426 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -73,7 +73,7 @@ ProcessFieldData Process_fields[] = { [M_SHARE] = { .name = "M_SHARE", .title = " SHR ", .description = "Size of the process's shared pages", .flags = 0, }, [M_TRS] = { .name = "M_TRS", .title = " CODE ", .description = "Size of the text segment of the process", .flags = 0, }, [M_DRS] = { .name = "M_DRS", .title = " DATA ", .description = "Size of the data segment plus stack usage of the process", .flags = 0, }, - [M_LRS] = { .name = "M_LRS", .title = " LIB ", .description = "The library size of the process (unused since Linux 2.6; always 0)", .flags = 0, }, + [M_LRS] = { .name = "M_LRS", .title = " LIB ", .description = "The library size of the process (calculated from memory maps)", .flags = PROCESS_FLAG_LINUX_LRS_FIX, }, [M_DT] = { .name = "M_DT", .title = " DIRTY ", .description = "Size of the dirty pages of the process (unused since Linux 2.6; always 0)", .flags = 0, }, [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, }, diff --git a/linux/LinuxProcess.h b/linux/LinuxProcess.h index 4c23d13c..68c01ea5 100644 --- a/linux/LinuxProcess.h +++ b/linux/LinuxProcess.h @@ -19,14 +19,15 @@ in the source distribution for its full text. #include "Settings.h" -#define PROCESS_FLAG_LINUX_IOPRIO 0x0100 -#define PROCESS_FLAG_LINUX_OPENVZ 0x0200 -#define PROCESS_FLAG_LINUX_VSERVER 0x0400 -#define PROCESS_FLAG_LINUX_CGROUP 0x0800 -#define PROCESS_FLAG_LINUX_OOM 0x1000 -#define PROCESS_FLAG_LINUX_SMAPS 0x2000 -#define PROCESS_FLAG_LINUX_CTXT 0x4000 -#define PROCESS_FLAG_LINUX_SECATTR 0x8000 +#define PROCESS_FLAG_LINUX_IOPRIO 0x00000100 +#define PROCESS_FLAG_LINUX_OPENVZ 0x00000200 +#define PROCESS_FLAG_LINUX_VSERVER 0x00000400 +#define PROCESS_FLAG_LINUX_CGROUP 0x00000800 +#define PROCESS_FLAG_LINUX_OOM 0x00001000 +#define PROCESS_FLAG_LINUX_SMAPS 0x00002000 +#define PROCESS_FLAG_LINUX_CTXT 0x00004000 +#define PROCESS_FLAG_LINUX_SECATTR 0x00008000 +#define PROCESS_FLAG_LINUX_LRS_FIX 0x00010000 typedef enum UnsupportedProcessFields { FLAGS = 9, diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index 1eb29763..6962527c 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -13,6 +13,7 @@ in the source distribution for its full text. #include #include #include +#include #include #include #include @@ -23,6 +24,7 @@ in the source distribution for its full text. #include #include #include +#include #ifdef HAVE_DELAYACCT #include @@ -479,7 +481,74 @@ static void LinuxProcessList_readIoFile(LinuxProcess* process, const char* dirna } } -static bool LinuxProcessList_readStatmFile(LinuxProcess* process, const char* dirname, const char* name) { +typedef struct LibraryData { + uint64_t size; + bool exec; +} LibraryData; + +static void LinuxProcessList_calcLibSize_helper(ATTR_UNUSED hkey_t key, void* value, void* data) { + if (!data) + return; + + if (!value) + return; + + LibraryData* v = (LibraryData *)value; + uint64_t* d = (uint64_t *)data; + if (!v->exec) + return; + + *d += v->size; +} + +static uint64_t LinuxProcessList_calcLibSize(const char* dirname, const char* name) { + char filename[MAX_NAME+1]; + xSnprintf(filename, sizeof(filename), "%s/%s/maps", dirname, name); + FILE* mapsfile = fopen(filename, "r"); + if (!mapsfile) + return 0; + + Hashtable* ht = Hashtable_new(64, true); + + char buffer[1024]; + while (fgets(buffer, sizeof(buffer), mapsfile)) { + uint64_t map_start; + uint64_t map_end; + char map_perm[16]; + uint32_t map_dummy; + int map_devmaj; + int map_devmin; + uint64_t map_inode; + + if (7 != sscanf(buffer, "%"PRIx64"-%"PRIx64" %4s %"PRIx32" %d:%d %" PRIu64, + &map_start, &map_end, map_perm, &map_dummy, + &map_devmaj, &map_devmin, &map_inode)) + continue; + + if (!map_inode) + continue; + + LibraryData * libdata; + libdata = Hashtable_get(ht, map_inode); + if (!libdata) { + libdata = xCalloc(1, sizeof(LibraryData)); + Hashtable_put(ht, map_inode, libdata); + } + + libdata->size += map_end - map_start; + libdata->exec |= 'x' == map_perm[2]; + } + + uint64_t total_size = 0; + Hashtable_foreach(ht, LinuxProcessList_calcLibSize_helper, &total_size); + + Hashtable_delete(ht); + + fclose(mapsfile); + return total_size / CRT_pageSize; +} + +static bool LinuxProcessList_readStatmFile(LinuxProcess* process, const char* dirname, const char* name, bool performLookup) { char filename[MAX_NAME + 1]; xSnprintf(filename, sizeof(filename), "%s/%s/statm", dirname, name); FILE* statmfile = fopen(filename, "r"); @@ -495,6 +564,9 @@ static bool LinuxProcessList_readStatmFile(LinuxProcess* process, const char* di &process->m_drs, &process->m_dt); fclose(statmfile); + if (r == 7 && !process->m_lrs && performLookup) { + process->m_lrs = LinuxProcessList_calcLibSize(dirname, name); + } return r == 7; } @@ -1193,7 +1265,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char* if (settings->flags & PROCESS_FLAG_IO) LinuxProcessList_readIoFile(lp, dirname, name, now); - if (! LinuxProcessList_readStatmFile(lp, dirname, name)) + if (!LinuxProcessList_readStatmFile(lp, dirname, name, !!(settings->flags & PROCESS_FLAG_LINUX_LRS_FIX))) goto errorReadingProcess; if ((settings->flags & PROCESS_FLAG_LINUX_SMAPS) && !Process_isKernelThread(proc)) {