From e205f7c3786bc4d7a0f660c01f4099d9f6f9baa7 Mon Sep 17 00:00:00 2001 From: Melissa Kilby Date: Wed, 17 Jan 2024 01:24:29 +0000 Subject: [PATCH 1/5] new(libsinsp): add concatenated process lineage fields as filter / display fields * Less loops and more efficient if you want to export all process ancestors up to a certain level anyways * More convenient and intuitive display of information -> security analysts prefer an output like this /bin/java->/bin/bash->/bin/python->/bin/bash to quickly understand the process origins * Enhanced threat detection capabilities -> can string match exact lineage / sequence * Needed in the planned anomaly detection framework Signed-off-by: Melissa Kilby --- .../libsinsp/sinsp_filtercheck_thread.cpp | 167 +++++++++++++++++- userspace/libsinsp/sinsp_filtercheck_thread.h | 3 + userspace/libsinsp/test/events_proc.ut.cpp | 9 + 3 files changed, 178 insertions(+), 1 deletion(-) diff --git a/userspace/libsinsp/sinsp_filtercheck_thread.cpp b/userspace/libsinsp/sinsp_filtercheck_thread.cpp index 41f5754de9..5437960fcc 100644 --- a/userspace/libsinsp/sinsp_filtercheck_thread.cpp +++ b/userspace/libsinsp/sinsp_filtercheck_thread.cpp @@ -49,12 +49,15 @@ static const filtercheck_field_info sinsp_filter_check_thread_fields[] = {PT_CHARBUF, EPF_NONE, PF_NA, "proc.exe", "First Argument", "The first command line argument argv[0] (truncated after 4096 bytes) which is usually the executable name but it could be also a custom string, it depends on what the user specifies. This field is collected from the syscalls args or, as a fallback, extracted from /proc/PID/cmdline."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.pexe", "Parent First Argument", "The proc.exe (first command line argument argv[0]) of the parent process."}, {PT_CHARBUF, EPF_ARG_ALLOWED, PF_NA, "proc.aexe", "Ancestor First Argument", "The proc.exe (first command line argument argv[0]) for a specific process ancestor. You can access different levels of ancestors by using indices. For example, proc.aexe[1] retrieves the proc.exe of the parent process, proc.aexe[2] retrieves the proc.exe of the grandparent process, and so on. The current process's proc.exe line can be obtained using proc.aexe[0]. When used without any arguments, proc.aexe is applicable only in filters and matches any of the process ancestors. For instance, you can use `proc.aexe endswith java` to match any process ancestor whose proc.exe ends with the term `java`."}, + {PT_CHARBUF, EPF_ARG_ALLOWED, PF_NA, "proc.concat_aexe", "Ancestor First Argument Concatenated", "The proc.exe lineage is traced back to a specific process ancestor, up to the specified level of ancestors. The result is a string representing the concatenated lineage, with '->' as the delimiter, starting with the furthest ancestor and including the current process. For example, proc.concat_aexe[3] returns the string `/bin/java->/bin/bash->/bin/python->/bin/bash`, assuming a java process launched a bash process that ultimately launched python and finally bash."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.exepath", "Process Executable Path", "The full executable path of the process (it could be truncated after 1024 bytes if read from '/proc'). This field is collected directly from the kernel or, as a fallback, extracted resolving the path of /proc/PID/exe, so symlinks are resolved. If you are using eBPF drivers this path could be truncated due to verifier complexity limits. (legacy eBPF kernel version < 5.2) truncated after 24 path components. (legacy eBPF kernel version >= 5.2) truncated after 48 path components. (modern eBPF kernel) truncated after 96 path components."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.pexepath", "Parent Process Executable Path", "The proc.exepath (full executable path) of the parent process."}, {PT_CHARBUF, EPF_ARG_ALLOWED, PF_NA, "proc.aexepath", "Ancestor Executable Path", "The proc.exepath (full executable path) for a specific process ancestor. You can access different levels of ancestors by using indices. For example, proc.aexepath[1] retrieves the proc.exepath of the parent process, proc.aexepath[2] retrieves the proc.exepath of the grandparent process, and so on. The current process's proc.exepath line can be obtained using proc.aexepath[0]. When used without any arguments, proc.aexepath is applicable only in filters and matches any of the process ancestors. For instance, you can use `proc.aexepath endswith java` to match any process ancestor whose path ends with the term `java`."}, + {PT_CHARBUF, EPF_ARG_ALLOWED, PF_NA, "proc.concat_aexepath", "Ancestor Executable Path Concatenated", "The proc.exepath lineage is traced back to a specific process ancestor, up to the specified level of ancestors. The result is a string representing the concatenated lineage, with '->' as the delimiter, starting with the furthest ancestor and including the current process. For example, proc.concat_aexepath[3] returns the string `/bin/java->/bin/bash->/bin/python->/bin/bash`, assuming a java process launched a bash process that ultimately launched python and finally bash."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.name", "Name", "The process name (truncated after 16 characters) generating the event (task->comm). Truncation is determined by kernel settings and not by Falco. This field is collected from the syscalls args or, as a fallback, extracted from /proc/PID/status. The name of the process and the name of the executable file on disk (if applicable) can be different if a process is given a custom name which is often the case for example for java applications."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.pname", "Parent Name", "The proc.name truncated after 16 characters) of the process generating the event."}, {PT_CHARBUF, EPF_ARG_ALLOWED, PF_NA, "proc.aname", "Ancestor Name", "The proc.name (truncated after 16 characters) for a specific process ancestor. You can access different levels of ancestors by using indices. For example, proc.aname[1] retrieves the proc.name of the parent process, proc.aname[2] retrieves the proc.name of the grandparent process, and so on. The current process's proc.name line can be obtained using proc.aname[0]. When used without any arguments, proc.aname is applicable only in filters and matches any of the process ancestors. For instance, you can use `proc.aname=bash` to match any process ancestor whose name is `bash`."}, + {PT_CHARBUF, EPF_ARG_ALLOWED, PF_NA, "proc.concat_aname", "Ancestor Name Concatenated", "The proc.name (truncated after 16 characters) lineage is traced back to a specific process ancestor, up to the specified level of ancestors. The result is a string representing the concatenated lineage, with '->' as the delimiter, starting with the furthest ancestor and including the current process. For example, proc.concat_aname[3] returns the string `java->bash->python->bash`, assuming a java process launched a bash process that ultimately launched python and finally bash."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.args", "Arguments", "The arguments passed on the command line when starting the process generating the event excluding argv[0] (truncated after 4096 bytes). This field is collected from the syscalls args or, as a fallback, extracted from /proc/PID/cmdline."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.cmdline", "Command Line", "The concatenation of `proc.name + proc.args` (truncated after 4096 bytes) when starting the process generating the event."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.pcmdline", "Parent Command Line", "The proc.cmdline (full command line (proc.name + proc.args)) of the parent of the process generating the event."}, @@ -155,7 +158,10 @@ int32_t sinsp_filter_check_thread::extract_arg(std::string fldname, std::string m_field_id == TYPE_ANAME || m_field_id == TYPE_AEXE || m_field_id == TYPE_AEXEPATH || - m_field_id == TYPE_ACMDLINE) + m_field_id == TYPE_ACMDLINE || + m_field_id == TYPE_CONCAT_ANAME || + m_field_id == TYPE_CONCAT_AEXE || + m_field_id == TYPE_CONCAT_AEXEPATH) { if(val[fldname.size()] == '[') { @@ -279,6 +285,28 @@ int32_t sinsp_filter_check_thread::parse_field_name(const char* str, bool alloc_ return res; } + else if(STR_MATCH("proc.concat_aname")) + { + m_field_id = TYPE_CONCAT_ANAME; + m_field = &m_info.m_fields[m_field_id]; + + int32_t res = 0; + + try + { + res = extract_arg("proc.concat_aname", val, NULL); + } + catch(...) + { + if(val == "proc.concat_aname") + { + m_argid = -1; + res = (int32_t)val.size(); + } + } + + return res; + } else if(STR_MATCH("proc.aexepath")) { m_field_id = TYPE_AEXEPATH; @@ -301,6 +329,28 @@ int32_t sinsp_filter_check_thread::parse_field_name(const char* str, bool alloc_ return res; } + else if(STR_MATCH("proc.concat_aexepath")) + { + m_field_id = TYPE_CONCAT_AEXEPATH; + m_field = &m_info.m_fields[m_field_id]; + + int32_t res = 0; + + try + { + res = extract_arg("proc.concat_aexepath", val, NULL); + } + catch(...) + { + if(val == "proc.concat_aexepath") + { + m_argid = -1; + res = (int32_t)val.size(); + } + } + + return res; + } /* note: because of str similarity of proc.aexe to proc.aexepath, this needs to be placed after proc.aexepath */ else if(STR_MATCH("proc.aexe")) { @@ -324,6 +374,28 @@ int32_t sinsp_filter_check_thread::parse_field_name(const char* str, bool alloc_ return res; } + else if(STR_MATCH("proc.concat_aexe")) + { + m_field_id = TYPE_CONCAT_AEXEPATH; + m_field = &m_info.m_fields[m_field_id]; + + int32_t res = 0; + + try + { + res = extract_arg("proc.concat_aexe", val, NULL); + } + catch(...) + { + if(val == "proc.concat_aexe") + { + m_argid = -1; + res = (int32_t)val.size(); + } + } + + return res; + } else if(STR_MATCH("proc.acmdline")) { m_field_id = TYPE_ACMDLINE; @@ -1145,6 +1217,37 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b m_tstr = mt->get_comm(); RETURN_EXTRACT_STRING(m_tstr); } + case TYPE_CONCAT_ANAME: + { + sinsp_threadinfo* mt = NULL; + + if(tinfo->is_main_thread()) + { + mt = tinfo; + } + else + { + mt = tinfo->get_main_thread(); + + if(mt == NULL) + { + return NULL; + } + } + + m_tstr = mt->get_comm(); + for(int32_t j = 0; j < m_argid; j++) + { + mt = mt->get_parent_thread(); + + if(mt == NULL) + { + RETURN_EXTRACT_STRING(m_tstr); + } + m_tstr = mt->get_comm() + "->" + m_tstr; + } + RETURN_EXTRACT_STRING(m_tstr); + } case TYPE_PEXE: { sinsp_threadinfo* ptinfo = @@ -1191,6 +1294,37 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b m_tstr = mt->get_exe(); RETURN_EXTRACT_STRING(m_tstr); } + case TYPE_CONCAT_AEXE: + { + sinsp_threadinfo* mt = NULL; + + if(tinfo->is_main_thread()) + { + mt = tinfo; + } + else + { + mt = tinfo->get_main_thread(); + + if(mt == NULL) + { + return NULL; + } + } + + m_tstr = mt->get_exe(); + for(int32_t j = 0; j < m_argid; j++) + { + mt = mt->get_parent_thread(); + + if(mt == NULL) + { + RETURN_EXTRACT_STRING(m_tstr); + } + m_tstr = mt->get_exe() + "->" + m_tstr; + } + RETURN_EXTRACT_STRING(m_tstr); + } case TYPE_PEXEPATH: { sinsp_threadinfo* ptinfo = @@ -1237,6 +1371,37 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b m_tstr = mt->get_exepath(); RETURN_EXTRACT_STRING(m_tstr); } + case TYPE_CONCAT_AEXEPATH: + { + sinsp_threadinfo* mt = NULL; + + if(tinfo->is_main_thread()) + { + mt = tinfo; + } + else + { + mt = tinfo->get_main_thread(); + + if(mt == NULL) + { + return NULL; + } + } + + m_tstr = mt->get_exepath(); + for(int32_t j = 0; j < m_argid; j++) + { + mt = mt->get_parent_thread(); + + if(mt == NULL) + { + RETURN_EXTRACT_STRING(m_tstr); + } + m_tstr = mt->get_exepath() + "->" + m_tstr; + } + RETURN_EXTRACT_STRING(m_tstr); + } case TYPE_LOGINSHELLID: { sinsp_threadinfo* mt = NULL; diff --git a/userspace/libsinsp/sinsp_filtercheck_thread.h b/userspace/libsinsp/sinsp_filtercheck_thread.h index 7f519edb28..7312fe522c 100644 --- a/userspace/libsinsp/sinsp_filtercheck_thread.h +++ b/userspace/libsinsp/sinsp_filtercheck_thread.h @@ -29,12 +29,15 @@ class sinsp_filter_check_thread : public sinsp_filter_check TYPE_EXE = 0, TYPE_PEXE, TYPE_AEXE, + TYPE_CONCAT_AEXE, TYPE_EXEPATH, TYPE_PEXEPATH, TYPE_AEXEPATH, + TYPE_CONCAT_AEXEPATH, TYPE_NAME, TYPE_PNAME, TYPE_ANAME, + TYPE_CONCAT_ANAME, TYPE_ARGS, TYPE_CMDLINE, TYPE_PCMDLINE, diff --git a/userspace/libsinsp/test/events_proc.ut.cpp b/userspace/libsinsp/test/events_proc.ut.cpp index 1cc88128bc..6c061927db 100644 --- a/userspace/libsinsp/test/events_proc.ut.cpp +++ b/userspace/libsinsp/test/events_proc.ut.cpp @@ -352,6 +352,9 @@ TEST_F(sinsp_with_test_input, spawn_process) ASSERT_EQ(get_field_as_string(evt, "proc.name"), "test-exe"); ASSERT_EQ(get_field_as_string(evt, "proc.aname[0]"), "test-exe"); ASSERT_EQ(get_field_as_string(evt, "proc.aname"), "test-exe"); + ASSERT_EQ(get_field_as_string(evt, "proc.concat_aname[0]"), "test-exe"); + ASSERT_EQ(get_field_as_string(evt, "proc.concat_aname[2]"), "init->test-exe"); + ASSERT_EQ(get_field_as_string(evt, "proc.concat_aname"), "test-exe"); // check that the pid is updated ASSERT_EQ(get_field_as_string(evt, "proc.pid"), "20"); @@ -363,11 +366,17 @@ TEST_F(sinsp_with_test_input, spawn_process) ASSERT_EQ(get_field_as_string(evt, "proc.exe"), "/bin/test-exe"); ASSERT_EQ(get_field_as_string(evt, "proc.aexe[0]"), "/bin/test-exe"); ASSERT_EQ(get_field_as_string(evt, "proc.aexe"), "/bin/test-exe"); + ASSERT_EQ(get_field_as_string(evt, "proc.concat_aexe[0]"), "/bin/test-exe"); + ASSERT_EQ(get_field_as_string(evt, "proc.concat_aexe[2]"), "/sbin/init->/bin/test-exe"); + ASSERT_EQ(get_field_as_string(evt, "proc.concat_aexe"), "/bin/test-exe"); // check that the exepath is updated ASSERT_EQ(get_field_as_string(evt, "proc.exepath"), "/bin/test-exe"); ASSERT_EQ(get_field_as_string(evt, "proc.aexepath[0]"), "/bin/test-exe"); ASSERT_EQ(get_field_as_string(evt, "proc.aexepath"), "/bin/test-exe"); + ASSERT_EQ(get_field_as_string(evt, "proc.concat_aexepath[0]"), "/bin/test-exe"); + ASSERT_EQ(get_field_as_string(evt, "proc.concat_aexepath[2]"), "/sbin/init->/bin/test-exe"); + ASSERT_EQ(get_field_as_string(evt, "proc.concat_aexepath"), "/bin/test-exe"); // check session leader (sid) related fields ASSERT_EQ(get_field_as_string(evt, "proc.sid"), "0"); From f19ca758122f17bf064a9eca90b7267453778e2d Mon Sep 17 00:00:00 2001 From: Melissa Kilby Date: Sat, 20 Jan 2024 00:26:08 +0000 Subject: [PATCH 2/5] cleanup(libsinsp): add sinsp_filter_check_thread::get_main_thread helper Signed-off-by: Melissa Kilby --- .../libsinsp/sinsp_filtercheck_thread.cpp | 253 +++++------------- userspace/libsinsp/sinsp_filtercheck_thread.h | 1 + 2 files changed, 63 insertions(+), 191 deletions(-) diff --git a/userspace/libsinsp/sinsp_filtercheck_thread.cpp b/userspace/libsinsp/sinsp_filtercheck_thread.cpp index 5437960fcc..ce73ef5942 100644 --- a/userspace/libsinsp/sinsp_filtercheck_thread.cpp +++ b/userspace/libsinsp/sinsp_filtercheck_thread.cpp @@ -584,6 +584,19 @@ uint8_t* sinsp_filter_check_thread::extract_thread_cpu(sinsp_evt *evt, OUT uint3 return NULL; } +sinsp_threadinfo* sinsp_filter_check_thread::get_main_thread(sinsp_threadinfo* tinfo) +{ + if (tinfo->is_main_thread()) + { + return tinfo; + } + else + { + sinsp_threadinfo* mt = tinfo->get_main_thread(); + return mt != nullptr ? mt : nullptr; + } +} + // Some syscall sources, such as the gVisor integration, cannot match events to host PIDs and TIDs. // The event will retain the PID field which is consistent with the rest of sinsp logic, but it won't represent // a real PID and so it should not be displayed to the user. @@ -932,18 +945,10 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b } // get current tinfo / init for subsequent parent lineage traversal - sinsp_threadinfo* mt = NULL; - if(tinfo->is_main_thread()) + sinsp_threadinfo* mt = get_main_thread(tinfo); + if(mt == NULL) { - mt = tinfo; - } - else - { - mt = tinfo->get_main_thread(); - if(mt == NULL) - { - RETURN_EXTRACT_STRING(m_tstr); - } + RETURN_EXTRACT_STRING(m_tstr); } if(!m_argname.empty()) // extract a specific ENV_NAME value @@ -1121,20 +1126,12 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b } case TYPE_ACMDLINE: { - sinsp_threadinfo* mt = NULL; - if(tinfo->is_main_thread()) - { - mt = tinfo; - } - else - { - mt = tinfo->get_main_thread(); + sinsp_threadinfo* mt = get_main_thread(tinfo); - if(mt == NULL) - { - return NULL; - } + if(mt == NULL) + { + return NULL; } for(int32_t j = 0; j < m_argid; j++) @@ -1151,20 +1148,11 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b } case TYPE_APID: { - sinsp_threadinfo* mt = NULL; + sinsp_threadinfo* mt = get_main_thread(tinfo); - if(tinfo->is_main_thread()) + if (mt == NULL) { - mt = tinfo; - } - else - { - mt = tinfo->get_main_thread(); - - if(mt == NULL) - { - return NULL; - } + return NULL; } // @@ -1188,20 +1176,11 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b } case TYPE_ANAME: { - sinsp_threadinfo* mt = NULL; + sinsp_threadinfo* mt = get_main_thread(tinfo); - if(tinfo->is_main_thread()) + if (mt == NULL) { - mt = tinfo; - } - else - { - mt = tinfo->get_main_thread(); - - if(mt == NULL) - { - return NULL; - } + return NULL; } for(int32_t j = 0; j < m_argid; j++) @@ -1219,20 +1198,11 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b } case TYPE_CONCAT_ANAME: { - sinsp_threadinfo* mt = NULL; + sinsp_threadinfo* mt = get_main_thread(tinfo); - if(tinfo->is_main_thread()) + if (mt == NULL) { - mt = tinfo; - } - else - { - mt = tinfo->get_main_thread(); - - if(mt == NULL) - { - return NULL; - } + return NULL; } m_tstr = mt->get_comm(); @@ -1265,20 +1235,11 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b } case TYPE_AEXE: { - sinsp_threadinfo* mt = NULL; + sinsp_threadinfo* mt = get_main_thread(tinfo); - if(tinfo->is_main_thread()) + if (mt == NULL) { - mt = tinfo; - } - else - { - mt = tinfo->get_main_thread(); - - if(mt == NULL) - { - return NULL; - } + return NULL; } for(int32_t j = 0; j < m_argid; j++) @@ -1296,20 +1257,11 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b } case TYPE_CONCAT_AEXE: { - sinsp_threadinfo* mt = NULL; + sinsp_threadinfo* mt = get_main_thread(tinfo); - if(tinfo->is_main_thread()) + if (mt == NULL) { - mt = tinfo; - } - else - { - mt = tinfo->get_main_thread(); - - if(mt == NULL) - { - return NULL; - } + return NULL; } m_tstr = mt->get_exe(); @@ -1342,20 +1294,11 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b } case TYPE_AEXEPATH: { - sinsp_threadinfo* mt = NULL; + sinsp_threadinfo* mt = get_main_thread(tinfo); - if(tinfo->is_main_thread()) - { - mt = tinfo; - } - else + if (mt == NULL) { - mt = tinfo->get_main_thread(); - - if(mt == NULL) - { - return NULL; - } + return NULL; } for(int32_t j = 0; j < m_argid; j++) @@ -1373,20 +1316,11 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b } case TYPE_CONCAT_AEXEPATH: { - sinsp_threadinfo* mt = NULL; + sinsp_threadinfo* mt = get_main_thread(tinfo); - if(tinfo->is_main_thread()) + if (mt == NULL) { - mt = tinfo; - } - else - { - mt = tinfo->get_main_thread(); - - if(mt == NULL) - { - return NULL; - } + return NULL; } m_tstr = mt->get_exepath(); @@ -1404,21 +1338,12 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b } case TYPE_LOGINSHELLID: { - sinsp_threadinfo* mt = NULL; int64_t* res = NULL; + sinsp_threadinfo* mt = get_main_thread(tinfo); - if(tinfo->is_main_thread()) + if (mt == NULL) { - mt = tinfo; - } - else - { - mt = tinfo->get_main_thread(); - - if(mt == NULL) - { - return NULL; - } + return NULL; } sinsp_threadinfo::visitor_func_t check_thread_for_shell = [&res] (sinsp_threadinfo *pt) @@ -1789,20 +1714,11 @@ bool sinsp_filter_check_thread::compare_full_apid(sinsp_evt *evt) return false; } - sinsp_threadinfo* mt = NULL; + sinsp_threadinfo* mt = get_main_thread(tinfo); - if(tinfo->is_main_thread()) + if (mt == NULL) { - mt = tinfo; - } - else - { - mt = tinfo->get_main_thread(); - - if(mt == NULL) - { - return false; - } + return false; } // @@ -1842,20 +1758,11 @@ bool sinsp_filter_check_thread::compare_full_aname(sinsp_evt *evt) return false; } - sinsp_threadinfo* mt = NULL; + sinsp_threadinfo* mt = get_main_thread(tinfo); - if(tinfo->is_main_thread()) - { - mt = tinfo; - } - else + if (mt == NULL) { - mt = tinfo->get_main_thread(); - - if(mt == NULL) - { - return false; - } + return false; } // @@ -1895,20 +1802,11 @@ bool sinsp_filter_check_thread::compare_full_aexe(sinsp_evt *evt) return false; } - sinsp_threadinfo* mt = NULL; + sinsp_threadinfo* mt = get_main_thread(tinfo); - if(tinfo->is_main_thread()) + if (mt == NULL) { - mt = tinfo; - } - else - { - mt = tinfo->get_main_thread(); - - if(mt == NULL) - { - return false; - } + return false; } // @@ -1948,20 +1846,11 @@ bool sinsp_filter_check_thread::compare_full_aexepath(sinsp_evt *evt) return false; } - sinsp_threadinfo* mt = NULL; + sinsp_threadinfo* mt = get_main_thread(tinfo); - if(tinfo->is_main_thread()) - { - mt = tinfo; - } - else + if (mt == NULL) { - mt = tinfo->get_main_thread(); - - if(mt == NULL) - { - return false; - } + return false; } // @@ -2001,20 +1890,11 @@ bool sinsp_filter_check_thread::compare_full_acmdline(sinsp_evt *evt) return false; } - sinsp_threadinfo* mt = NULL; + sinsp_threadinfo* mt = get_main_thread(tinfo); - if(tinfo->is_main_thread()) + if (mt == NULL) { - mt = tinfo; - } - else - { - mt = tinfo->get_main_thread(); - - if(mt == NULL) - { - return false; - } + return false; } // @@ -2056,20 +1936,11 @@ bool sinsp_filter_check_thread::compare_full_aenv(sinsp_evt *evt) return false; } - sinsp_threadinfo* mt = NULL; + sinsp_threadinfo* mt = get_main_thread(tinfo); - if(tinfo->is_main_thread()) + if (mt == NULL) { - mt = tinfo; - } - else - { - mt = tinfo->get_main_thread(); - - if(mt == NULL) - { - return false; - } + return false; } // diff --git a/userspace/libsinsp/sinsp_filtercheck_thread.h b/userspace/libsinsp/sinsp_filtercheck_thread.h index 7312fe522c..c6a2b6521a 100644 --- a/userspace/libsinsp/sinsp_filtercheck_thread.h +++ b/userspace/libsinsp/sinsp_filtercheck_thread.h @@ -127,6 +127,7 @@ class sinsp_filter_check_thread : public sinsp_filter_check uint64_t extract_exectime(sinsp_evt *evt); int32_t extract_arg(std::string fldname, std::string val, OUT const struct ppm_param_info** parinfo); uint8_t* extract_thread_cpu(sinsp_evt *evt, OUT uint32_t* len, sinsp_threadinfo* tinfo, bool extract_user, bool extract_system); + sinsp_threadinfo* get_main_thread(sinsp_threadinfo* tinfo); inline bool compare_full_apid(sinsp_evt *evt); bool compare_full_aname(sinsp_evt *evt); bool compare_full_aexe(sinsp_evt *evt); From 6b99e18335076e68b3678bf0c6d6d49e07b24de7 Mon Sep 17 00:00:00 2001 From: Melissa Kilby Date: Sat, 20 Jan 2024 01:26:49 +0000 Subject: [PATCH 3/5] cleanup(libsinsp): add concat_attribute_thread_hierarchy helper Signed-off-by: Melissa Kilby --- .../libsinsp/sinsp_filtercheck_thread.cpp | 37 ++----------------- userspace/libsinsp/sinsp_filtercheck_thread.h | 22 +++++++++++ 2 files changed, 25 insertions(+), 34 deletions(-) diff --git a/userspace/libsinsp/sinsp_filtercheck_thread.cpp b/userspace/libsinsp/sinsp_filtercheck_thread.cpp index ce73ef5942..10708b5cb3 100644 --- a/userspace/libsinsp/sinsp_filtercheck_thread.cpp +++ b/userspace/libsinsp/sinsp_filtercheck_thread.cpp @@ -1204,18 +1204,7 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b { return NULL; } - - m_tstr = mt->get_comm(); - for(int32_t j = 0; j < m_argid; j++) - { - mt = mt->get_parent_thread(); - - if(mt == NULL) - { - RETURN_EXTRACT_STRING(m_tstr); - } - m_tstr = mt->get_comm() + "->" + m_tstr; - } + m_tstr = concat_attribute_thread_hierarchy(mt, m_argid, [](sinsp_threadinfo* t) { return t->get_comm(); }); RETURN_EXTRACT_STRING(m_tstr); } case TYPE_PEXE: @@ -1264,17 +1253,7 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b return NULL; } - m_tstr = mt->get_exe(); - for(int32_t j = 0; j < m_argid; j++) - { - mt = mt->get_parent_thread(); - - if(mt == NULL) - { - RETURN_EXTRACT_STRING(m_tstr); - } - m_tstr = mt->get_exe() + "->" + m_tstr; - } + m_tstr = concat_attribute_thread_hierarchy(mt, m_argid, [](sinsp_threadinfo* t) { return t->get_exe(); }); RETURN_EXTRACT_STRING(m_tstr); } case TYPE_PEXEPATH: @@ -1323,17 +1302,7 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b return NULL; } - m_tstr = mt->get_exepath(); - for(int32_t j = 0; j < m_argid; j++) - { - mt = mt->get_parent_thread(); - - if(mt == NULL) - { - RETURN_EXTRACT_STRING(m_tstr); - } - m_tstr = mt->get_exepath() + "->" + m_tstr; - } + m_tstr = concat_attribute_thread_hierarchy(mt, m_argid, [](sinsp_threadinfo* t) { return t->get_exepath(); }); RETURN_EXTRACT_STRING(m_tstr); } case TYPE_LOGINSHELLID: diff --git a/userspace/libsinsp/sinsp_filtercheck_thread.h b/userspace/libsinsp/sinsp_filtercheck_thread.h index c6a2b6521a..41d426d22c 100644 --- a/userspace/libsinsp/sinsp_filtercheck_thread.h +++ b/userspace/libsinsp/sinsp_filtercheck_thread.h @@ -18,6 +18,7 @@ limitations under the License. #pragma once +#include #include #include @@ -135,6 +136,27 @@ class sinsp_filter_check_thread : public sinsp_filter_check bool compare_full_acmdline(sinsp_evt *evt); bool compare_full_aenv(sinsp_evt *evt); + template + std::string concat_attribute_thread_hierarchy(sinsp_threadinfo* mt, int32_t m_argid, const std::function& get_attribute_func) + { + + // nullptr check of mt is done within each filtercheck prior to calling this function + static_assert(std::is_convertible::value, "T must be convertible to std::string to concat parent lineage thread attributes"); + std::string result = get_attribute_func(mt); + + for (int32_t j = 0; j < m_argid; j++) + { + mt = mt->get_parent_thread(); + if(mt == NULL) + { + return result; + } + result = get_attribute_func(mt) + "->" + result; + } + + return result; + } + int32_t m_argid; std::string m_argname; uint32_t m_tbool; From 8a35134b5848c5f9502492ff9bc0757df44b9698 Mon Sep 17 00:00:00 2001 From: Melissa Kilby Date: Sat, 20 Jan 2024 02:17:00 +0000 Subject: [PATCH 4/5] cleanup(libsinsp): add extract_leader_attribute_thread_hierarchy helper Signed-off-by: Melissa Kilby --- .../libsinsp/sinsp_filtercheck_thread.cpp | 232 +----------------- userspace/libsinsp/sinsp_filtercheck_thread.h | 38 ++- 2 files changed, 42 insertions(+), 228 deletions(-) diff --git a/userspace/libsinsp/sinsp_filtercheck_thread.cpp b/userspace/libsinsp/sinsp_filtercheck_thread.cpp index 10708b5cb3..0d48deb097 100644 --- a/userspace/libsinsp/sinsp_filtercheck_thread.cpp +++ b/userspace/libsinsp/sinsp_filtercheck_thread.cpp @@ -639,253 +639,33 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b RETURN_EXTRACT_VAR(tinfo->m_vpgid); case TYPE_SNAME: { - int64_t sid = tinfo->m_sid; - - if(!tinfo->is_in_pid_namespace()) - { - // Relying on the convention that a session id is the process id of the session leader. - // `threadinfo` lookup only applies when the process is running on the host and not in a pid - // namespace. However, if the process is running in a pid namespace, we instead traverse the process - // lineage until we find a match. - sinsp_threadinfo* sinfo = m_inspector->get_thread_ref(sid, false, true).get(); - if(sinfo != NULL) - { - m_tstr = sinfo->get_comm(); - RETURN_EXTRACT_STRING(m_tstr); - } - } - - // This can occur when the session leader process has exited or if the process - // is running in a pid namespace and we only have the virtual session id, as - // seen from its pid namespace. - // Find the highest ancestor process that has the same session id and - // declare it to be the session leader. - sinsp_threadinfo* session_leader = tinfo; - - sinsp_threadinfo::visitor_func_t visitor = [sid, &session_leader](sinsp_threadinfo* pt) - { - if(pt->m_sid != sid) - { - return false; - } - session_leader = pt; - return true; - }; - - tinfo->traverse_parent_state(visitor); - - // session_leader has been updated to the highest process that has the same session id. - // session_leader's comm is considered the session leader. - m_tstr = session_leader->get_comm(); + m_tstr = extract_leader_attribute_thread_hierarchy(tinfo, [](sinsp_threadinfo* t) { return t->m_sid; }, [](sinsp_threadinfo* t) { return t->get_comm(); }); RETURN_EXTRACT_STRING(m_tstr); } case TYPE_SID_EXE: { - int64_t sid = tinfo->m_sid; - - if(!tinfo->is_in_pid_namespace()) - { - // Relying on the convention that a session id is the process id of the session leader. - // `threadinfo` lookup only applies when the process is running on the host and not in a pid - // namespace. However, if the process is running in a pid namespace, we instead traverse the process - // lineage until we find a match. - sinsp_threadinfo* sinfo = m_inspector->get_thread_ref(sid, false, true).get(); - if(sinfo != NULL) - { - m_tstr = sinfo->get_exe(); - RETURN_EXTRACT_STRING(m_tstr); - } - } - - // This can occur when the session leader process has exited or if the process - // is running in a pid namespace and we only have the virtual session id, as - // seen from its pid namespace. - // Find the highest ancestor process that has the same session id and - // declare it to be the session leader. - sinsp_threadinfo* session_leader = tinfo; - - sinsp_threadinfo::visitor_func_t visitor = [sid, &session_leader](sinsp_threadinfo* pt) - { - if(pt->m_sid != sid) - { - return false; - } - session_leader = pt; - return true; - }; - - tinfo->traverse_parent_state(visitor); - - // session_leader has been updated to the highest process that has the same session id. - // session_leader's exe is considered the session leader. - m_tstr = session_leader->get_exe(); + m_tstr = extract_leader_attribute_thread_hierarchy(tinfo, [](sinsp_threadinfo* t) { return t->m_sid; }, [](sinsp_threadinfo* t) { return t->get_exe(); }); RETURN_EXTRACT_STRING(m_tstr); } case TYPE_SID_EXEPATH: { - int64_t sid = tinfo->m_sid; - - if(!tinfo->is_in_pid_namespace()) - { - // Relying on the convention that a session id is the process id of the session leader. - // `threadinfo` lookup only applies when the process is running on the host and not in a pid - // namespace. However, if the process is running in a pid namespace, we instead traverse the process - // lineage until we find a match. - sinsp_threadinfo* sinfo = m_inspector->get_thread_ref(sid, false, true).get(); - if(sinfo != NULL) - { - m_tstr = sinfo->get_exepath(); - RETURN_EXTRACT_STRING(m_tstr); - } - } - - // This can occur when the session leader process has exited or if the process - // is running in a pid namespace and we only have the virtual session id, as - // seen from its pid namespace. - // Find the highest ancestor process that has the same session id and - // declare it to be the session leader. - sinsp_threadinfo* session_leader = tinfo; - - sinsp_threadinfo::visitor_func_t visitor = [sid, &session_leader](sinsp_threadinfo* pt) - { - if(pt->m_sid != sid) - { - return false; - } - session_leader = pt; - return true; - }; - - tinfo->traverse_parent_state(visitor); - - // session_leader has been updated to the highest process that has the same session id. - // session_leader's exepath is considered the session leader. - m_tstr = session_leader->get_exepath(); + m_tstr = extract_leader_attribute_thread_hierarchy(tinfo, [](sinsp_threadinfo* t) { return t->m_sid; }, [](sinsp_threadinfo* t) { return t->get_exepath(); }); RETURN_EXTRACT_STRING(m_tstr); } case TYPE_VPGID_NAME: { - int64_t vpgid = tinfo->m_vpgid; - - if(!tinfo->is_in_pid_namespace()) - { - // Relying on the convention that a process group id is the process id of the process group leader. - // `threadinfo` lookup only applies when the process is running on the host and not in a pid - // namespace. However, if the process is running in a pid namespace, we instead traverse the process - // lineage until we find a match. - sinsp_threadinfo* vpgidinfo = m_inspector->get_thread_ref(vpgid, false, true).get(); - if(vpgidinfo != NULL) - { - m_tstr = vpgidinfo->get_comm(); - RETURN_EXTRACT_STRING(m_tstr); - } - } - // This can occur when the process group leader process has exited or if the process - // is running in a pid namespace and we only have the virtual process group id, as - // seen from its pid namespace. - // Find the highest ancestor process that has the same process group id and - // declare it to be the process group leader. - sinsp_threadinfo* group_leader = tinfo; - - sinsp_threadinfo::visitor_func_t visitor = [vpgid, &group_leader](sinsp_threadinfo* pt) - { - if(pt->m_vpgid != vpgid) - { - return false; - } - group_leader = pt; - return true; - }; - - tinfo->traverse_parent_state(visitor); - - // group_leader has been updated to the highest process that has the same process group id. - // group_leader's comm is considered the process group leader. - m_tstr = group_leader->get_comm(); + m_tstr = extract_leader_attribute_thread_hierarchy(tinfo, [](sinsp_threadinfo* t) { return t->m_vpgid; }, [](sinsp_threadinfo* t) { return t->get_comm(); }); RETURN_EXTRACT_STRING(m_tstr); } case TYPE_VPGID_EXE: { - int64_t vpgid = tinfo->m_vpgid; - - if(!tinfo->is_in_pid_namespace()) - { - // Relying on the convention that a process group id is the process id of the process group leader. - // `threadinfo` lookup only applies when the process is running on the host and not in a pid - // namespace. However, if the process is running in a pid namespace, we instead traverse the process - // lineage until we find a match. - sinsp_threadinfo* vpgidinfo = m_inspector->get_thread_ref(vpgid, false, true).get(); - if(vpgidinfo != NULL) - { - m_tstr = vpgidinfo->get_exe(); - RETURN_EXTRACT_STRING(m_tstr); - } - } - // This can occur when the process group leader process has exited or if the process - // is running in a pid namespace and we only have the virtual process group id, as - // seen from its pid namespace. - // Find the highest ancestor process that has the same process group id and - // declare it to be the process group leader. - sinsp_threadinfo* group_leader = tinfo; - - sinsp_threadinfo::visitor_func_t visitor = [vpgid, &group_leader](sinsp_threadinfo* pt) - { - if(pt->m_vpgid != vpgid) - { - return false; - } - group_leader = pt; - return true; - }; - - tinfo->traverse_parent_state(visitor); - - // group_leader has been updated to the highest process that has the same process group id. - // group_leader's exe is considered the process group leader. - m_tstr = group_leader->get_exe(); + m_tstr = extract_leader_attribute_thread_hierarchy(tinfo, [](sinsp_threadinfo* t) { return t->m_vpgid; }, [](sinsp_threadinfo* t) { return t->get_exe(); }); RETURN_EXTRACT_STRING(m_tstr); } case TYPE_VPGID_EXEPATH: { - int64_t vpgid = tinfo->m_vpgid; - - if(!tinfo->is_in_pid_namespace()) - { - // Relying on the convention that a process group id is the process id of the process group leader. - // `threadinfo` lookup only applies when the process is running on the host and not in a pid - // namespace. However, if the process is running in a pid namespace, we instead traverse the process - // lineage until we find a match. - sinsp_threadinfo* vpgidinfo = m_inspector->get_thread_ref(vpgid, false, true).get(); - if(vpgidinfo != NULL) - { - m_tstr = vpgidinfo->get_exepath(); - RETURN_EXTRACT_STRING(m_tstr); - } - } - - // This can occur when the process group leader process has exited or if the process - // is running in a pid namespace and we only have the virtual process group id, as - // seen from its pid namespace. - // Find the highest ancestor process that has the same process group id and - // declare it to be the process group leader. - sinsp_threadinfo* group_leader = tinfo; - - sinsp_threadinfo::visitor_func_t visitor = [vpgid, &group_leader](sinsp_threadinfo* pt) - { - if(pt->m_vpgid != vpgid) - { - return false; - } - group_leader = pt; - return true; - }; - - tinfo->traverse_parent_state(visitor); - - // group_leader has been updated to the highest process that has the same process group id. - // group_leader's exepath is considered the process group leader. - m_tstr = group_leader->get_exepath(); + m_tstr = extract_leader_attribute_thread_hierarchy(tinfo, [](sinsp_threadinfo* t) { return t->m_vpgid; }, [](sinsp_threadinfo* t) { return t->get_exepath(); }); RETURN_EXTRACT_STRING(m_tstr); } case TYPE_TTY: diff --git a/userspace/libsinsp/sinsp_filtercheck_thread.h b/userspace/libsinsp/sinsp_filtercheck_thread.h index 41d426d22c..d9d5e8f280 100644 --- a/userspace/libsinsp/sinsp_filtercheck_thread.h +++ b/userspace/libsinsp/sinsp_filtercheck_thread.h @@ -139,9 +139,8 @@ class sinsp_filter_check_thread : public sinsp_filter_check template std::string concat_attribute_thread_hierarchy(sinsp_threadinfo* mt, int32_t m_argid, const std::function& get_attribute_func) { - // nullptr check of mt is done within each filtercheck prior to calling this function - static_assert(std::is_convertible::value, "T must be convertible to std::string to concat parent lineage thread attributes"); + static_assert(std::is_convertible::value, "T must be convertible to std::string"); std::string result = get_attribute_func(mt); for (int32_t j = 0; j < m_argid; j++) @@ -157,6 +156,41 @@ class sinsp_filter_check_thread : public sinsp_filter_check return result; } + template + std::string extract_leader_attribute_thread_hierarchy(sinsp_threadinfo* tinfo, const std::function& get_thread_attribute_func, const std::function& get_attribute_func) + { + // nullptr check of tinfo is done prior to calling this function + static_assert(std::is_convertible::value, "T must be convertible to std::string"); + int64_t thread_attribute = get_thread_attribute_func(tinfo); + + if (!tinfo->is_in_pid_namespace()) + { + // `threadinfo` lookup only applies when the process is running on the host and not in a PID namespace. + sinsp_threadinfo* attribute_info = m_inspector->get_thread_ref(thread_attribute, false, true).get(); + if (attribute_info != NULL) + { + return get_attribute_func(attribute_info); + } + } + + // However, if the leader process has exited or if the process is running in a PID namespace, we instead + // traverse the process lineage until we find a match. + // Find the highest ancestor process that has the same id and declare it to be the leader. + sinsp_threadinfo* leader = tinfo; + sinsp_threadinfo::visitor_func_t visitor = [thread_attribute, &leader, get_thread_attribute_func](sinsp_threadinfo* pt) + { + if (get_thread_attribute_func(pt) != thread_attribute) + { + return false; + } + leader = pt; + return true; + }; + + tinfo->traverse_parent_state(visitor); + return get_attribute_func(leader); + } + int32_t m_argid; std::string m_argname; uint32_t m_tbool; From 0e1cc4dcb5c54bb36febb2aedb312c739c6d4d8a Mon Sep 17 00:00:00 2001 From: Andrea Terzolo Date: Thu, 7 Mar 2024 20:11:28 +0100 Subject: [PATCH 5/5] update: move some methods and remove some duplication Signed-off-by: Andrea Terzolo --- .../libsinsp/sinsp_filtercheck_thread.cpp | 120 +++++++++--------- userspace/libsinsp/sinsp_filtercheck_thread.h | 57 --------- userspace/libsinsp/threadinfo.cpp | 67 +++++++++- userspace/libsinsp/threadinfo.h | 19 ++- 4 files changed, 139 insertions(+), 124 deletions(-) diff --git a/userspace/libsinsp/sinsp_filtercheck_thread.cpp b/userspace/libsinsp/sinsp_filtercheck_thread.cpp index 0d48deb097..3afa2a1b3d 100644 --- a/userspace/libsinsp/sinsp_filtercheck_thread.cpp +++ b/userspace/libsinsp/sinsp_filtercheck_thread.cpp @@ -584,19 +584,6 @@ uint8_t* sinsp_filter_check_thread::extract_thread_cpu(sinsp_evt *evt, OUT uint3 return NULL; } -sinsp_threadinfo* sinsp_filter_check_thread::get_main_thread(sinsp_threadinfo* tinfo) -{ - if (tinfo->is_main_thread()) - { - return tinfo; - } - else - { - sinsp_threadinfo* mt = tinfo->get_main_thread(); - return mt != nullptr ? mt : nullptr; - } -} - // Some syscall sources, such as the gVisor integration, cannot match events to host PIDs and TIDs. // The event will retain the PID field which is consistent with the rest of sinsp logic, but it won't represent // a real PID and so it should not be displayed to the user. @@ -639,34 +626,63 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b RETURN_EXTRACT_VAR(tinfo->m_vpgid); case TYPE_SNAME: { - m_tstr = extract_leader_attribute_thread_hierarchy(tinfo, [](sinsp_threadinfo* t) { return t->m_sid; }, [](sinsp_threadinfo* t) { return t->get_comm(); }); - RETURN_EXTRACT_STRING(m_tstr); + auto session_leader = tinfo->get_oldest_matching_ancestor([](sinsp_threadinfo* t) {return t->m_sid;}); + if(session_leader) + { + m_tstr = session_leader->get_comm(); + RETURN_EXTRACT_STRING(m_tstr); + } + return nullptr; } case TYPE_SID_EXE: { - m_tstr = extract_leader_attribute_thread_hierarchy(tinfo, [](sinsp_threadinfo* t) { return t->m_sid; }, [](sinsp_threadinfo* t) { return t->get_exe(); }); - RETURN_EXTRACT_STRING(m_tstr); + auto session_leader = tinfo->get_oldest_matching_ancestor([](sinsp_threadinfo* t) {return t->m_sid;}); + if(session_leader) + { + m_tstr = session_leader->get_exe(); + RETURN_EXTRACT_STRING(m_tstr); + } + return nullptr; } case TYPE_SID_EXEPATH: { - m_tstr = extract_leader_attribute_thread_hierarchy(tinfo, [](sinsp_threadinfo* t) { return t->m_sid; }, [](sinsp_threadinfo* t) { return t->get_exepath(); }); - RETURN_EXTRACT_STRING(m_tstr); + auto session_leader = tinfo->get_oldest_matching_ancestor([](sinsp_threadinfo* t) {return t->m_sid;}); + if(session_leader) + { + m_tstr = session_leader->get_exepath(); + RETURN_EXTRACT_STRING(m_tstr); + } + return nullptr; } case TYPE_VPGID_NAME: { - m_tstr = extract_leader_attribute_thread_hierarchy(tinfo, [](sinsp_threadinfo* t) { return t->m_vpgid; }, [](sinsp_threadinfo* t) { return t->get_comm(); }); - RETURN_EXTRACT_STRING(m_tstr); + auto group_leader = tinfo->get_oldest_matching_ancestor([](sinsp_threadinfo* t) {return t->m_vpgid;}); + if(group_leader) + { + m_tstr = group_leader->get_comm(); + RETURN_EXTRACT_STRING(m_tstr); + } + return nullptr; } case TYPE_VPGID_EXE: { - m_tstr = extract_leader_attribute_thread_hierarchy(tinfo, [](sinsp_threadinfo* t) { return t->m_vpgid; }, [](sinsp_threadinfo* t) { return t->get_exe(); }); - RETURN_EXTRACT_STRING(m_tstr); - + auto group_leader = tinfo->get_oldest_matching_ancestor([](sinsp_threadinfo* t) {return t->m_vpgid;}); + if(group_leader) + { + m_tstr = group_leader->get_exe(); + RETURN_EXTRACT_STRING(m_tstr); + } + return nullptr; } case TYPE_VPGID_EXEPATH: { - m_tstr = extract_leader_attribute_thread_hierarchy(tinfo, [](sinsp_threadinfo* t) { return t->m_vpgid; }, [](sinsp_threadinfo* t) { return t->get_exepath(); }); - RETURN_EXTRACT_STRING(m_tstr); + auto group_leader = tinfo->get_oldest_matching_ancestor([](sinsp_threadinfo* t) {return t->m_vpgid;}); + if(group_leader) + { + m_tstr = group_leader->get_exepath(); + RETURN_EXTRACT_STRING(m_tstr); + } + return nullptr; } case TYPE_TTY: RETURN_EXTRACT_VAR(tinfo->m_tty); @@ -725,7 +741,7 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b } // get current tinfo / init for subsequent parent lineage traversal - sinsp_threadinfo* mt = get_main_thread(tinfo); + sinsp_threadinfo* mt = tinfo->get_main_thread(); if(mt == NULL) { RETURN_EXTRACT_STRING(m_tstr); @@ -907,7 +923,7 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b case TYPE_ACMDLINE: { - sinsp_threadinfo* mt = get_main_thread(tinfo); + sinsp_threadinfo* mt = tinfo->get_main_thread(); if(mt == NULL) { @@ -928,7 +944,7 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b } case TYPE_APID: { - sinsp_threadinfo* mt = get_main_thread(tinfo); + sinsp_threadinfo* mt = tinfo->get_main_thread(); if (mt == NULL) { @@ -956,7 +972,7 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b } case TYPE_ANAME: { - sinsp_threadinfo* mt = get_main_thread(tinfo); + sinsp_threadinfo* mt = tinfo->get_main_thread(); if (mt == NULL) { @@ -978,13 +994,7 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b } case TYPE_CONCAT_ANAME: { - sinsp_threadinfo* mt = get_main_thread(tinfo); - - if (mt == NULL) - { - return NULL; - } - m_tstr = concat_attribute_thread_hierarchy(mt, m_argid, [](sinsp_threadinfo* t) { return t->get_comm(); }); + m_tstr = tinfo->concat_attribute_thread_lineage([](sinsp_threadinfo* t) {return t->get_comm();}, m_argid); RETURN_EXTRACT_STRING(m_tstr); } case TYPE_PEXE: @@ -1004,7 +1014,7 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b } case TYPE_AEXE: { - sinsp_threadinfo* mt = get_main_thread(tinfo); + sinsp_threadinfo* mt = tinfo->get_main_thread(); if (mt == NULL) { @@ -1026,14 +1036,7 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b } case TYPE_CONCAT_AEXE: { - sinsp_threadinfo* mt = get_main_thread(tinfo); - - if (mt == NULL) - { - return NULL; - } - - m_tstr = concat_attribute_thread_hierarchy(mt, m_argid, [](sinsp_threadinfo* t) { return t->get_exe(); }); + m_tstr = tinfo->concat_attribute_thread_lineage([](sinsp_threadinfo* t) {return t->get_exe();}, m_argid); RETURN_EXTRACT_STRING(m_tstr); } case TYPE_PEXEPATH: @@ -1053,7 +1056,7 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b } case TYPE_AEXEPATH: { - sinsp_threadinfo* mt = get_main_thread(tinfo); + sinsp_threadinfo* mt = tinfo->get_main_thread(); if (mt == NULL) { @@ -1075,20 +1078,13 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b } case TYPE_CONCAT_AEXEPATH: { - sinsp_threadinfo* mt = get_main_thread(tinfo); - - if (mt == NULL) - { - return NULL; - } - - m_tstr = concat_attribute_thread_hierarchy(mt, m_argid, [](sinsp_threadinfo* t) { return t->get_exepath(); }); + m_tstr = tinfo->concat_attribute_thread_lineage([](sinsp_threadinfo* t) {return t->get_exepath();}, m_argid); RETURN_EXTRACT_STRING(m_tstr); } case TYPE_LOGINSHELLID: { int64_t* res = NULL; - sinsp_threadinfo* mt = get_main_thread(tinfo); + sinsp_threadinfo* mt = tinfo->get_main_thread(); if (mt == NULL) { @@ -1463,7 +1459,7 @@ bool sinsp_filter_check_thread::compare_full_apid(sinsp_evt *evt) return false; } - sinsp_threadinfo* mt = get_main_thread(tinfo); + sinsp_threadinfo* mt = tinfo->get_main_thread(); if (mt == NULL) { @@ -1507,7 +1503,7 @@ bool sinsp_filter_check_thread::compare_full_aname(sinsp_evt *evt) return false; } - sinsp_threadinfo* mt = get_main_thread(tinfo); + sinsp_threadinfo* mt = tinfo->get_main_thread(); if (mt == NULL) { @@ -1551,7 +1547,7 @@ bool sinsp_filter_check_thread::compare_full_aexe(sinsp_evt *evt) return false; } - sinsp_threadinfo* mt = get_main_thread(tinfo); + sinsp_threadinfo* mt = tinfo->get_main_thread(); if (mt == NULL) { @@ -1595,7 +1591,7 @@ bool sinsp_filter_check_thread::compare_full_aexepath(sinsp_evt *evt) return false; } - sinsp_threadinfo* mt = get_main_thread(tinfo); + sinsp_threadinfo* mt = tinfo->get_main_thread(); if (mt == NULL) { @@ -1639,7 +1635,7 @@ bool sinsp_filter_check_thread::compare_full_acmdline(sinsp_evt *evt) return false; } - sinsp_threadinfo* mt = get_main_thread(tinfo); + sinsp_threadinfo* mt = tinfo->get_main_thread(); if (mt == NULL) { @@ -1685,7 +1681,7 @@ bool sinsp_filter_check_thread::compare_full_aenv(sinsp_evt *evt) return false; } - sinsp_threadinfo* mt = get_main_thread(tinfo); + sinsp_threadinfo* mt = tinfo->get_main_thread(); if (mt == NULL) { diff --git a/userspace/libsinsp/sinsp_filtercheck_thread.h b/userspace/libsinsp/sinsp_filtercheck_thread.h index d9d5e8f280..7312fe522c 100644 --- a/userspace/libsinsp/sinsp_filtercheck_thread.h +++ b/userspace/libsinsp/sinsp_filtercheck_thread.h @@ -18,7 +18,6 @@ limitations under the License. #pragma once -#include #include #include @@ -128,7 +127,6 @@ class sinsp_filter_check_thread : public sinsp_filter_check uint64_t extract_exectime(sinsp_evt *evt); int32_t extract_arg(std::string fldname, std::string val, OUT const struct ppm_param_info** parinfo); uint8_t* extract_thread_cpu(sinsp_evt *evt, OUT uint32_t* len, sinsp_threadinfo* tinfo, bool extract_user, bool extract_system); - sinsp_threadinfo* get_main_thread(sinsp_threadinfo* tinfo); inline bool compare_full_apid(sinsp_evt *evt); bool compare_full_aname(sinsp_evt *evt); bool compare_full_aexe(sinsp_evt *evt); @@ -136,61 +134,6 @@ class sinsp_filter_check_thread : public sinsp_filter_check bool compare_full_acmdline(sinsp_evt *evt); bool compare_full_aenv(sinsp_evt *evt); - template - std::string concat_attribute_thread_hierarchy(sinsp_threadinfo* mt, int32_t m_argid, const std::function& get_attribute_func) - { - // nullptr check of mt is done within each filtercheck prior to calling this function - static_assert(std::is_convertible::value, "T must be convertible to std::string"); - std::string result = get_attribute_func(mt); - - for (int32_t j = 0; j < m_argid; j++) - { - mt = mt->get_parent_thread(); - if(mt == NULL) - { - return result; - } - result = get_attribute_func(mt) + "->" + result; - } - - return result; - } - - template - std::string extract_leader_attribute_thread_hierarchy(sinsp_threadinfo* tinfo, const std::function& get_thread_attribute_func, const std::function& get_attribute_func) - { - // nullptr check of tinfo is done prior to calling this function - static_assert(std::is_convertible::value, "T must be convertible to std::string"); - int64_t thread_attribute = get_thread_attribute_func(tinfo); - - if (!tinfo->is_in_pid_namespace()) - { - // `threadinfo` lookup only applies when the process is running on the host and not in a PID namespace. - sinsp_threadinfo* attribute_info = m_inspector->get_thread_ref(thread_attribute, false, true).get(); - if (attribute_info != NULL) - { - return get_attribute_func(attribute_info); - } - } - - // However, if the leader process has exited or if the process is running in a PID namespace, we instead - // traverse the process lineage until we find a match. - // Find the highest ancestor process that has the same id and declare it to be the leader. - sinsp_threadinfo* leader = tinfo; - sinsp_threadinfo::visitor_func_t visitor = [thread_attribute, &leader, get_thread_attribute_func](sinsp_threadinfo* pt) - { - if (get_thread_attribute_func(pt) != thread_attribute) - { - return false; - } - leader = pt; - return true; - }; - - tinfo->traverse_parent_state(visitor); - return get_attribute_func(leader); - } - int32_t m_argid; std::string m_argname; uint32_t m_tbool; diff --git a/userspace/libsinsp/threadinfo.cpp b/userspace/libsinsp/threadinfo.cpp index 7557fca4e6..952aaea884 100644 --- a/userspace/libsinsp/threadinfo.cpp +++ b/userspace/libsinsp/threadinfo.cpp @@ -102,10 +102,10 @@ sinsp_threadinfo::sinsp_threadinfo(sinsp* inspector, std::shared_ptr& get_attribute_func, int32_t max_level) +{ + auto mt = get_main_thread(); + if(mt == NULL) + { + return ""; + } + + std::string result = get_attribute_func(mt); + for (int32_t j = 0; j < max_level; j++) + { + mt = mt->get_parent_thread(); + if(mt == NULL) + { + return result; + } + result = get_attribute_func(mt) + "->" + result; + } + + return result; +} + +sinsp_threadinfo* sinsp_threadinfo::get_oldest_matching_ancestor(const std::function& get_thread_id, bool query_os_if_not_found) +{ + int64_t id = get_thread_id(this); + if(id == - 1) + { + // the id is not set + return nullptr; + } + + // If in the init namespace we can use the id for the thread table access + // try this first since it is faster! + sinsp_threadinfo* leader = nullptr; + if(!is_in_pid_namespace()) + { + leader = m_inspector->get_thread_ref(id, query_os_if_not_found).get(); + if(leader!=nullptr) + { + return leader; + } + } + + // If we are in a pid_namespace we cannot use directly m_sid to access the table + // since it could be related to a pid namespace. + sinsp_threadinfo::visitor_func_t visitor = [id, &leader, get_thread_id](sinsp_threadinfo* pt) + { + if(get_thread_id(pt) != id) + { + return false; + } + leader = pt; + return true; + }; + + traverse_parent_state(visitor); + return leader; +} + void sinsp_threadinfo::set_args(const char* args, size_t len) { m_args.clear(); diff --git a/userspace/libsinsp/threadinfo.h b/userspace/libsinsp/threadinfo.h index a6897b9bb3..5d18c49f39 100644 --- a/userspace/libsinsp/threadinfo.h +++ b/userspace/libsinsp/threadinfo.h @@ -245,6 +245,23 @@ class SINSP_PUBLIC sinsp_threadinfo : public libsinsp::state::table_entry return possible_main; } + std::string concat_attribute_thread_lineage(const std::function& get_attribute_func, int32_t max_level); + + /*! + \brief Return the "oldest ancestor" of this thread that matches a condition. + The "oldest ancestor" is the oldest thread in the lineage that matches the following condition: + `get_thread_id(this) = get_thread_id(ancestor)` + + \param get_thread_id custom function to select the ancestor we want to extract. + If we want to obtain the oldest session leader ancestor we can pass: + `[](sinsp_threadinfo* t) { return t->m_sid; }` + + \param query_os_if_not_found if this is a live a capture and this flag is + set to true, scan the /proc file system to find process information in + case the thread is not in the table. + */ + sinsp_threadinfo* get_oldest_matching_ancestor(const std::function& get_thread_id, bool query_os_if_not_found = false); + inline const sinsp_threadinfo* get_main_thread() const { return const_cast(this)->get_main_thread(); @@ -418,7 +435,7 @@ class SINSP_PUBLIC sinsp_threadinfo : public libsinsp::state::table_entry int64_t m_pid; ///< The id of the process containing this thread. In single thread threads, this is equal to tid. int64_t m_ptid; ///< The id of the process that started this thread. int64_t m_reaper_tid; ///< The id of the reaper for this thread - int64_t m_sid; ///< The session id of the process containing this thread. + int64_t m_sid; ///< The session id of the process containing this thread. Please note that this is referred to the pid_namespace in which the process is running, is not always referred to the init_pid_namespace, so it cannot be used as a key in our thread table. std::string m_comm; ///< Command name (e.g. "top") std::string m_exe; ///< argv[0] (e.g. "sshd: user@pts/4") std::string m_exepath; ///< full executable path