diff --git a/driver/main.c b/driver/main.c index 303519a684..722c2621b0 100644 --- a/driver/main.c +++ b/driver/main.c @@ -167,6 +167,7 @@ static unsigned int g_ppm_numdevs; static int g_ppm_major; bool g_tracers_enabled = false; bool g_simple_mode_enabled = false; +static DEFINE_PER_CPU(long, g_n_tracepoint_hit); static const struct file_operations g_ppm_fops = { .open = ppm_open, .release = ppm_release, @@ -518,6 +519,7 @@ static int ppm_open(struct inode *inode, struct file *filp) static int ppm_release(struct inode *inode, struct file *filp) { + int cpu; int ret; struct ppm_ring_buffer_context *ring; #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) @@ -604,6 +606,13 @@ static int ppm_release(struct inode *inode, struct file *filp) * While we're here, disable simple mode if it's active */ g_simple_mode_enabled = false; + + /* + * Reset tracepoint counter + */ + for_each_possible_cpu(cpu) { + per_cpu(g_n_tracepoint_hit, cpu) = 0; + } } else { ASSERT(false); } @@ -619,6 +628,7 @@ static int ppm_release(struct inode *inode, struct file *filp) static long ppm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { + int cpu; int ret; struct task_struct *consumer_id = filp->private_data; struct ppm_consumer_t *consumer = NULL; @@ -723,7 +733,20 @@ static long ppm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ret = 0; cleanup_ioctl_procinfo: - vfree((void*)proclist_info); + vfree((void *)proclist_info); + goto cleanup_ioctl_nolock; + } + + if (cmd == PPM_IOCTL_GET_N_TRACEPOINT_HIT) { + long __user *counters = (long __user *) arg; + + for_each_possible_cpu(cpu) { + if (put_user(per_cpu(g_n_tracepoint_hit, cpu), &counters[cpu])) { + ret = -EINVAL; + goto cleanup_ioctl_nolock; + } + } + ret = 0; goto cleanup_ioctl_nolock; } @@ -1772,6 +1795,19 @@ static int record_event_consumer(struct ppm_consumer_t *consumer, return res; } +static inline void g_n_tracepoint_hit_inc(void) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) + this_cpu_inc(g_n_tracepoint_hit); +#else + /* + * per_cpu_var removed with: + * https://github.com/torvalds/linux/commit/dd17c8f72993f9461e9c19250e3f155d6d99df22 + */ + this_cpu_inc(per_cpu_var(g_n_tracepoint_hit)); +#endif +} + TRACEPOINT_PROBE(syscall_enter_probe, struct pt_regs *regs, long id) { long table_index; @@ -1790,7 +1826,7 @@ TRACEPOINT_PROBE(syscall_enter_probe, struct pt_regs *regs, long id) * kernel flag), we switch to the ia32 syscall table. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) - if(in_ia32_syscall()) { + if (in_ia32_syscall()) { #else if (unlikely(task_thread_info(current)->status & TS_COMPAT)) { #endif @@ -1812,7 +1848,7 @@ TRACEPOINT_PROBE(syscall_enter_probe, struct pt_regs *regs, long id) * Simple mode event filtering */ if (g_simple_mode_enabled) { - if((drop_flags & UF_SIMPLEDRIVER_KEEP) == 0) { + if ((drop_flags & UF_SIMPLEDRIVER_KEEP) == 0) { return; } } @@ -1865,7 +1901,7 @@ TRACEPOINT_PROBE(syscall_exit_probe, struct pt_regs *regs, long ret) * which is a very old syscall, not used anymore by most applications */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) - if(in_ia32_syscall() && id != __NR_execve) { + if (in_ia32_syscall() && id != __NR_execve) { #else if (unlikely((task_thread_info(current)->status & TS_COMPAT) && id != __NR_execve)) { #endif @@ -1876,6 +1912,8 @@ TRACEPOINT_PROBE(syscall_exit_probe, struct pt_regs *regs, long ret) } #endif + g_n_tracepoint_hit_inc(); + table_index = id - SYSCALL_TABLE_ID0; if (likely(table_index >= 0 && table_index < SYSCALL_TABLE_SIZE)) { struct event_data_t event_data; @@ -1887,7 +1925,7 @@ TRACEPOINT_PROBE(syscall_exit_probe, struct pt_regs *regs, long ret) * Simple mode event filtering */ if (g_simple_mode_enabled) { - if((drop_flags & UF_SIMPLEDRIVER_KEEP) == 0) { + if ((drop_flags & UF_SIMPLEDRIVER_KEEP) == 0) { return; } } @@ -1926,6 +1964,8 @@ TRACEPOINT_PROBE(syscall_procexit_probe, struct task_struct *p) { struct event_data_t event_data; + g_n_tracepoint_hit_inc(); + #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) if (unlikely(current->flags & PF_KTHREAD)) { #else @@ -1959,6 +1999,8 @@ TRACEPOINT_PROBE(sched_switch_probe, bool preempt, struct task_struct *prev, str { struct event_data_t event_data; + g_n_tracepoint_hit_inc(); + event_data.category = PPMC_CONTEXT_SWITCH; event_data.event_info.context_data.sched_prev = prev; event_data.event_info.context_data.sched_next = next; @@ -1972,6 +2014,8 @@ TRACEPOINT_PROBE(signal_deliver_probe, int sig, struct siginfo *info, struct k_s { struct event_data_t event_data; + g_n_tracepoint_hit_inc(); + event_data.category = PPMC_SIGNAL; event_data.event_info.signal_data.sig = sig; event_data.event_info.signal_data.info = info; @@ -1990,6 +2034,7 @@ TRACEPOINT_PROBE(page_fault_probe, unsigned long address, struct pt_regs *regs, * in the output by looking for the USER_FAULT/SUPERVISOR_FAULT * flags */ + g_n_tracepoint_hit_inc(); /* I still haven't decided if I'm interested in kernel threads or not. * For the moment, I assume yes since I can see some value for it. @@ -2325,7 +2370,7 @@ int sysdig_init(void) g_ppm_numdevs = num_cpus; #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) g_ppm_devs = kmalloc(g_ppm_numdevs * sizeof(struct ppm_device), GFP_KERNEL); -#else +#else g_ppm_devs = kmalloc_array(g_ppm_numdevs, sizeof(struct ppm_device), GFP_KERNEL); #endif if (!g_ppm_devs) { diff --git a/driver/ppm_events_public.h b/driver/ppm_events_public.h index 51d1ec0839..bd690253b5 100644 --- a/driver/ppm_events_public.h +++ b/driver/ppm_events_public.h @@ -1326,6 +1326,7 @@ struct ppm_evt_hdr { #define PPM_IOCTL_SET_TRACERS_CAPTURE _IO(PPM_IOCTL_MAGIC, 17) #define PPM_IOCTL_SET_SIMPLE_MODE _IO(PPM_IOCTL_MAGIC, 18) #define PPM_IOCTL_ENABLE_PAGE_FAULTS _IO(PPM_IOCTL_MAGIC, 19) +#define PPM_IOCTL_GET_N_TRACEPOINT_HIT _IO(PPM_IOCTL_MAGIC, 20) extern const struct ppm_name_value socket_families[]; extern const struct ppm_name_value file_flags[]; diff --git a/userspace/libscap/scap.c b/userspace/libscap/scap.c index 5d4d94e6b1..3a6d858258 100644 --- a/userspace/libscap/scap.c +++ b/userspace/libscap/scap.c @@ -1524,3 +1524,30 @@ int32_t scap_enable_simpledriver_mode(scap_t* handle) return SCAP_SUCCESS; #endif } + +int32_t scap_get_n_tracepoint_hit(scap_t* handle, long* ret) +{ + // + // Not supported on files + // + if(handle->m_mode != SCAP_MODE_LIVE) + { + snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "getting n_tracepoint_hit not supported on this scap mode"); + return SCAP_FAILURE; + } + +#if !defined(HAS_CAPTURE) + snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "live capture not supported on %s", PLATFORM_NAME); + return SCAP_FAILURE; +#else + + if(ioctl(handle->m_devs[0].m_fd, PPM_IOCTL_GET_N_TRACEPOINT_HIT, ret)) + { + snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_get_n_tracepoint_hit failed"); + ASSERT(false); + return SCAP_FAILURE; + } + + return SCAP_SUCCESS; +#endif +} diff --git a/userspace/libscap/scap.h b/userspace/libscap/scap.h index 734f7522e2..cfe0669326 100644 --- a/userspace/libscap/scap.h +++ b/userspace/libscap/scap.h @@ -944,6 +944,7 @@ int32_t scap_write_proclist_header(scap_t *handle, scap_dumper_t *d, uint32_t to int32_t scap_write_proclist_trailer(scap_t *handle, scap_dumper_t *d, uint32_t totlen); int32_t scap_write_proclist_entry(scap_t *handle, scap_dumper_t *d, struct scap_threadinfo *tinfo); int32_t scap_enable_simpledriver_mode(scap_t* handle); +int32_t scap_get_n_tracepoint_hit(scap_t* handle, long* ret); #ifdef __cplusplus } diff --git a/userspace/libsinsp/sinsp.cpp b/userspace/libsinsp/sinsp.cpp index 11dad004c8..c47a4e02cf 100644 --- a/userspace/libsinsp/sinsp.cpp +++ b/userspace/libsinsp/sinsp.cpp @@ -519,8 +519,34 @@ void sinsp::set_simpledriver_mode() { if(scap_enable_simpledriver_mode(m_h) != SCAP_SUCCESS) { - throw sinsp_exception(scap_getlasterr(m_h)); + throw sinsp_exception(scap_getlasterr(m_h)); + } +} + +unsigned sinsp::m_num_possible_cpus = 0; + +unsigned sinsp::num_possible_cpus() +{ + if(m_num_possible_cpus == 0) + { + m_num_possible_cpus = read_num_possible_cpus(); + if(m_num_possible_cpus == 0) + { + g_logger.log("Unable to read num_possible_cpus, falling back to 128", sinsp_logger::SEV_WARNING); + m_num_possible_cpus = 128; + } + } + return m_num_possible_cpus; +} + +vector sinsp::get_n_tracepoint_hit() +{ + vector ret(num_possible_cpus(), 0); + if(scap_get_n_tracepoint_hit(m_h, ret.data()) != SCAP_SUCCESS) + { + throw sinsp_exception(scap_getlasterr(m_h)); } + return ret; } std::string sinsp::get_error_desc(const std::string& msg) diff --git a/userspace/libsinsp/sinsp.h b/userspace/libsinsp/sinsp.h index 5b2113e47a..9e7b03c521 100644 --- a/userspace/libsinsp/sinsp.h +++ b/userspace/libsinsp/sinsp.h @@ -802,7 +802,9 @@ class SINSP_PUBLIC sinsp scap_refresh_proc_table(m_h); } void set_simpledriver_mode(); + vector get_n_tracepoint_hit(); + static unsigned num_possible_cpus(); VISIBILITY_PRIVATE // Doxygen doesn't understand VISIBILITY_PRIVATE @@ -1071,6 +1073,7 @@ VISIBILITY_PRIVATE uint64_t m_last_procrequest_tod; sinsp_proc_metainfo m_meinfo; + static unsigned int m_num_possible_cpus; #if defined(HAS_CAPTURE) int64_t m_sysdig_pid; #endif diff --git a/userspace/libsinsp/utils.cpp b/userspace/libsinsp/utils.cpp index 2901a9106d..8ee3b14f9f 100644 --- a/userspace/libsinsp/utils.cpp +++ b/userspace/libsinsp/utils.cpp @@ -1417,3 +1417,27 @@ bool set_socket_blocking(int sock, bool block) } return true; } + +unsigned int read_num_possible_cpus(void) +{ + static const char *fcpu = "/sys/devices/system/cpu/possible"; + unsigned int start, end, possible_cpus = 0; + char buff[128]; + FILE *fp; + + fp = fopen(fcpu, "r"); + if (!fp) { + return possible_cpus; + } + + while (fgets(buff, sizeof(buff), fp)) { + if (sscanf(buff, "%u-%u", &start, &end) == 2) { + possible_cpus = start == 0 ? end + 1 : 0; + break; + } + } + + fclose(fp); + + return possible_cpus; +} diff --git a/userspace/libsinsp/utils.h b/userspace/libsinsp/utils.h index 5685e9d7f6..f60cec013e 100644 --- a/userspace/libsinsp/utils.h +++ b/userspace/libsinsp/utils.h @@ -350,3 +350,5 @@ struct ci_compare /////////////////////////////////////////////////////////////////////////////// bool set_socket_blocking(int sock, bool block); + +unsigned int read_num_possible_cpus(void);