X-Git-Url: http://git.lttng.org/?p=lttng-modules.git;a=blobdiff_plain;f=lttng-statedump-impl.c;h=1a2a12ba7f650ceb9a633b2cd7f79729aeadec32;hp=c8ac2d12750d210a11a9c375d2cf0f0aed39e189;hb=cd4486798c2b046ea93b89439cd705e93c40b349;hpb=5a91f3dfb2ea8b50bfcba45c4440cf20735ea3ee diff --git a/lttng-statedump-impl.c b/lttng-statedump-impl.c index c8ac2d12..1a2a12ba 100644 --- a/lttng-statedump-impl.c +++ b/lttng-statedump-impl.c @@ -1,4 +1,5 @@ -/* +/* SPDX-License-Identifier: (GPL-2.0-only or LGPL-2.1-only) + * * lttng-statedump.c * * Linux Trace Toolkit Next Generation Kernel State Dump @@ -6,20 +7,6 @@ * Copyright 2005 Jean-Hugues Deschenes * Copyright 2006-2012 Mathieu Desnoyers * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; only - * version 2.1 of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * * Changes: * Eric Clement: Add listing of network IP interface * 2006, 2007 Mathieu Desnoyers Fix kernel threads @@ -41,21 +28,21 @@ #include #include #include -#include #include #include #include #include -#include "lttng-events.h" -#include "lttng-tracer.h" -#include "wrapper/irqdesc.h" -#include "wrapper/spinlock.h" -#include "wrapper/fdtable.h" -#include "wrapper/nsproxy.h" -#include "wrapper/irq.h" -#include "wrapper/tracepoint.h" -#include "wrapper/genhd.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #ifdef CONFIG_LTTNG_HAS_LIST_IRQ #include @@ -63,9 +50,10 @@ /* Define the tracepoints, but do not build the probes */ #define CREATE_TRACE_POINTS -#define TRACE_INCLUDE_PATH ../instrumentation/events/lttng-module +#define TRACE_INCLUDE_PATH instrumentation/events #define TRACE_INCLUDE_FILE lttng-statedump -#include "instrumentation/events/lttng-module/lttng-statedump.h" +#define LTTNG_INSTRUMENTATION +#include DEFINE_TRACE(lttng_statedump_block_device); DEFINE_TRACE(lttng_statedump_end); @@ -73,13 +61,26 @@ DEFINE_TRACE(lttng_statedump_interrupt); DEFINE_TRACE(lttng_statedump_file_descriptor); DEFINE_TRACE(lttng_statedump_start); DEFINE_TRACE(lttng_statedump_process_state); +DEFINE_TRACE(lttng_statedump_process_pid_ns); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,6,0)) +DEFINE_TRACE(lttng_statedump_process_cgroup_ns); +#endif +DEFINE_TRACE(lttng_statedump_process_ipc_ns); +#ifndef LTTNG_MNT_NS_MISSING_HEADER +DEFINE_TRACE(lttng_statedump_process_mnt_ns); +#endif +DEFINE_TRACE(lttng_statedump_process_net_ns); +DEFINE_TRACE(lttng_statedump_process_user_ns); +DEFINE_TRACE(lttng_statedump_process_uts_ns); DEFINE_TRACE(lttng_statedump_network_interface); +#ifdef LTTNG_HAVE_STATEDUMP_CPU_TOPOLOGY +DEFINE_TRACE(lttng_statedump_cpu_topology); +#endif struct lttng_fd_ctx { char *page; struct lttng_session *session; - struct task_struct *p; - struct fdtable *fdt; + struct files_struct *files; }; /* @@ -219,56 +220,74 @@ int lttng_dump_one_fd(const void *p, struct file *file, unsigned int fd) const struct lttng_fd_ctx *ctx = p; const char *s = d_path(&file->f_path, ctx->page, PAGE_SIZE); unsigned int flags = file->f_flags; + struct fdtable *fdt; /* * We don't expose kernel internal flags, only userspace-visible * flags. */ flags &= ~FMODE_NONOTIFY; - if (test_bit(fd, ctx->fdt->close_on_exec)) + fdt = files_fdtable(ctx->files); + /* + * We need to check here again whether fd is within the fdt + * max_fds range, because we might be seeing a different + * files_fdtable() than iterate_fd(), assuming only RCU is + * protecting the read. In reality, iterate_fd() holds + * file_lock, which should ensure the fdt does not change while + * the lock is taken, but we are not aware whether this is + * guaranteed or not, so play safe. + */ + if (fd < fdt->max_fds && lttng_close_on_exec(fd, fdt)) flags |= O_CLOEXEC; if (IS_ERR(s)) { struct dentry *dentry = file->f_path.dentry; /* Make sure we give at least some info */ spin_lock(&dentry->d_lock); - trace_lttng_statedump_file_descriptor(ctx->session, ctx->p, fd, - dentry->d_name.name, flags, file->f_mode); + trace_lttng_statedump_file_descriptor(ctx->session, + ctx->files, fd, dentry->d_name.name, flags, + file->f_mode); spin_unlock(&dentry->d_lock); goto end; } - trace_lttng_statedump_file_descriptor(ctx->session, ctx->p, fd, s, - flags, file->f_mode); + trace_lttng_statedump_file_descriptor(ctx->session, + ctx->files, fd, s, flags, file->f_mode); end: return 0; } +/* Called with task lock held. */ static -void lttng_enumerate_task_fd(struct lttng_session *session, - struct task_struct *p, char *tmp) +void lttng_enumerate_files(struct lttng_session *session, + struct files_struct *files, + char *tmp) { - struct lttng_fd_ctx ctx = { .page = tmp, .session = session, .p = p }; + struct lttng_fd_ctx ctx = { .page = tmp, .session = session, .files = files, }; - task_lock(p); - ctx.fdt = files_fdtable(p->files); - lttng_iterate_fd(p->files, 0, lttng_dump_one_fd, &ctx); - task_unlock(p); + lttng_iterate_fd(files, 0, lttng_dump_one_fd, &ctx); } +#ifdef LTTNG_HAVE_STATEDUMP_CPU_TOPOLOGY static -int lttng_enumerate_file_descriptors(struct lttng_session *session) +int lttng_enumerate_cpu_topology(struct lttng_session *session) { - struct task_struct *p; - char *tmp = (char *) __get_free_page(GFP_KERNEL); + int cpu; + const cpumask_t *cpumask = cpu_possible_mask; - /* Enumerate active file descriptors */ - rcu_read_lock(); - for_each_process(p) - lttng_enumerate_task_fd(session, p, tmp); - rcu_read_unlock(); - free_page((unsigned long) tmp); + for (cpu = cpumask_first(cpumask); cpu < nr_cpu_ids; + cpu = cpumask_next(cpu, cpumask)) { + trace_lttng_statedump_cpu_topology(session, &cpu_data(cpu)); + } + + return 0; +} +#else +static +int lttng_enumerate_cpu_topology(struct lttng_session *session) +{ return 0; } +#endif #if 0 /* @@ -294,7 +313,7 @@ void lttng_enumerate_task_vm_maps(struct lttng_session *session, down_read(&mm->mmap_sem); while (map) { if (map->vm_file) - ino = map->vm_file->f_dentry->d_inode->i_ino; + ino = map->vm_file->lttng_f_dentry->d_inode->i_ino; else ino = 0; trace_lttng_statedump_vm_map(session, p, map, ino); @@ -320,12 +339,8 @@ int lttng_enumerate_vm_maps(struct lttng_session *session) #ifdef CONFIG_LTTNG_HAS_LIST_IRQ -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) -#define irq_desc_get_chip(desc) get_irq_desc_chip(desc) -#endif - static -void lttng_list_interrupts(struct lttng_session *session) +int lttng_list_interrupts(struct lttng_session *session) { unsigned int irq; unsigned long flags = 0; @@ -339,23 +354,32 @@ void lttng_list_interrupts(struct lttng_session *session) irq_desc_get_chip(desc)->name ? : "unnamed_irq_chip"; local_irq_save(flags); - wrapper_desc_spin_lock(&desc->lock); + raw_spin_lock(&desc->lock); for (action = desc->action; action; action = action->next) { trace_lttng_statedump_interrupt(session, irq, irq_chip_name, action); } - wrapper_desc_spin_unlock(&desc->lock); + raw_spin_unlock(&desc->lock); local_irq_restore(flags); } + return 0; #undef irq_to_desc } #else static inline -void lttng_list_interrupts(struct lttng_session *session) +int lttng_list_interrupts(struct lttng_session *session) { + return 0; } #endif +/* + * Statedump the task's namespaces using the proc filesystem inode number as + * the unique identifier. The user and pid ns are nested and will be dumped + * recursively. + * + * Called with task lock held. + */ static void lttng_statedump_process_ns(struct lttng_session *session, struct task_struct *p, @@ -366,30 +390,85 @@ void lttng_statedump_process_ns(struct lttng_session *session, { struct nsproxy *proxy; struct pid_namespace *pid_ns; + struct user_namespace *user_ns; + + /* + * The pid and user namespaces are special, they are nested and + * accessed with specific functions instead of the nsproxy struct + * like the other namespaces. + */ + pid_ns = task_active_pid_ns(p); + do { + trace_lttng_statedump_process_pid_ns(session, p, pid_ns); + pid_ns = pid_ns ? pid_ns->parent : NULL; + } while (pid_ns); + + + user_ns = task_cred_xxx(p, user_ns); + do { + trace_lttng_statedump_process_user_ns(session, p, user_ns); + /* + * trace_lttng_statedump_process_user_ns() internally + * checks whether user_ns is NULL. While this does not + * appear to be a possible return value for + * task_cred_xxx(), err on the safe side and check + * for NULL here as well to be consistent with the + * paranoid behavior of + * trace_lttng_statedump_process_user_ns(). + */ + user_ns = user_ns ? user_ns->lttng_user_ns_parent : NULL; + } while (user_ns); + /* + * Back and forth on locking strategy within Linux upstream for nsproxy. + * See Linux upstream commit 728dba3a39c66b3d8ac889ddbe38b5b1c264aec3 + * "namespaces: Use task_lock and not rcu to protect nsproxy" + * for details. + */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0) || \ + LTTNG_UBUNTU_KERNEL_RANGE(3,13,11,36, 3,14,0,0) || \ + LTTNG_UBUNTU_KERNEL_RANGE(3,16,1,11, 3,17,0,0) || \ + LTTNG_RHEL_KERNEL_RANGE(3,10,0,229,13,0, 3,11,0,0,0,0)) + proxy = p->nsproxy; +#else rcu_read_lock(); proxy = task_nsproxy(p); +#endif if (proxy) { - pid_ns = lttng_get_proxy_pid_ns(proxy); - do { - trace_lttng_statedump_process_state(session, - p, type, mode, submode, status, pid_ns); - pid_ns = pid_ns->parent; - } while (pid_ns); - } else { - trace_lttng_statedump_process_state(session, - p, type, mode, submode, status, NULL); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,6,0)) + trace_lttng_statedump_process_cgroup_ns(session, p, proxy->cgroup_ns); +#endif + trace_lttng_statedump_process_ipc_ns(session, p, proxy->ipc_ns); +#ifndef LTTNG_MNT_NS_MISSING_HEADER + trace_lttng_statedump_process_mnt_ns(session, p, proxy->mnt_ns); +#endif + trace_lttng_statedump_process_net_ns(session, p, proxy->net_ns); + trace_lttng_statedump_process_uts_ns(session, p, proxy->uts_ns); } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0) || \ + LTTNG_UBUNTU_KERNEL_RANGE(3,13,11,36, 3,14,0,0) || \ + LTTNG_UBUNTU_KERNEL_RANGE(3,16,1,11, 3,17,0,0) || \ + LTTNG_RHEL_KERNEL_RANGE(3,10,0,229,13,0, 3,11,0,0,0,0)) + /* (nothing) */ +#else rcu_read_unlock(); +#endif } static int lttng_enumerate_process_states(struct lttng_session *session) { struct task_struct *g, *p; + char *tmp; + + tmp = (char *) __get_free_page(GFP_KERNEL); + if (!tmp) + return -ENOMEM; rcu_read_lock(); for_each_process(g) { + struct files_struct *prev_files = NULL; + p = g; do { enum lttng_execution_mode mode = @@ -398,6 +477,7 @@ int lttng_enumerate_process_states(struct lttng_session *session) LTTNG_UNKNOWN; enum lttng_process_status status; enum lttng_thread_type type; + struct files_struct *files; task_lock(p); if (p->exit_state == EXIT_ZOMBIE) @@ -432,13 +512,31 @@ int lttng_enumerate_process_states(struct lttng_session *session) type = LTTNG_USER_THREAD; else type = LTTNG_KERNEL_THREAD; + files = p->files; + + trace_lttng_statedump_process_state(session, + p, type, mode, submode, status, files); lttng_statedump_process_ns(session, p, type, mode, submode, status); + /* + * As an optimisation for the common case, do not + * repeat information for the same files_struct in + * two consecutive threads. This is the common case + * for threads sharing the same fd table. RCU guarantees + * that the same files_struct pointer is not re-used + * throughout processes/threads iteration. + */ + if (files && files != prev_files) { + lttng_enumerate_files(session, files, tmp); + prev_files = files; + } task_unlock(p); } while_each_thread(g, p); } rcu_read_unlock(); + free_page((unsigned long) tmp); + return 0; } @@ -453,15 +551,37 @@ void lttng_statedump_work_func(struct work_struct *work) static int do_lttng_statedump(struct lttng_session *session) { - int cpu; + int cpu, ret; trace_lttng_statedump_start(session); - lttng_enumerate_process_states(session); - lttng_enumerate_file_descriptors(session); - /* FIXME lttng_enumerate_vm_maps(session); */ - lttng_list_interrupts(session); - lttng_enumerate_network_ip_interface(session); - lttng_enumerate_block_devices(session); + ret = lttng_enumerate_process_states(session); + if (ret) + return ret; + /* + * FIXME + * ret = lttng_enumerate_vm_maps(session); + * if (ret) + * return ret; + */ + ret = lttng_list_interrupts(session); + if (ret) + return ret; + ret = lttng_enumerate_network_ip_interface(session); + if (ret) + return ret; + ret = lttng_enumerate_block_devices(session); + switch (ret) { + case 0: + break; + case -ENOSYS: + printk(KERN_WARNING "LTTng: block device enumeration is not supported by kernel\n"); + break; + default: + return ret; + } + ret = lttng_enumerate_cpu_topology(session); + if (ret) + return ret; /* TODO lttng_dump_idt_table(session); */ /* TODO lttng_dump_softirq_vec(session); */ @@ -520,7 +640,7 @@ module_exit(lttng_statedump_exit); MODULE_LICENSE("GPL and additional rights"); MODULE_AUTHOR("Jean-Hugues Deschenes"); -MODULE_DESCRIPTION("Linux Trace Toolkit Next Generation Statedump"); +MODULE_DESCRIPTION("LTTng statedump provider"); MODULE_VERSION(__stringify(LTTNG_MODULES_MAJOR_VERSION) "." __stringify(LTTNG_MODULES_MINOR_VERSION) "." __stringify(LTTNG_MODULES_PATCHLEVEL_VERSION)