#define LTTNG_INSTRUMENTATION
#include <instrumentation/events/lttng-module/lttng-statedump.h>
-DEFINE_TRACE(lttng_statedump_block_device);
-DEFINE_TRACE(lttng_statedump_end);
-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);
+LTTNG_DEFINE_TRACE(lttng_statedump_block_device,
+ TP_PROTO(struct lttng_session *session,
+ dev_t dev, const char *diskname),
+ TP_ARGS(session, dev, diskname));
+
+LTTNG_DEFINE_TRACE(lttng_statedump_end,
+ TP_PROTO(struct lttng_session *session),
+ TP_ARGS(session));
+
+LTTNG_DEFINE_TRACE(lttng_statedump_interrupt,
+ TP_PROTO(struct lttng_session *session,
+ unsigned int irq, const char *chip_name,
+ struct irqaction *action),
+ TP_ARGS(session, irq, chip_name, action));
+
+LTTNG_DEFINE_TRACE(lttng_statedump_file_descriptor,
+ TP_PROTO(struct lttng_session *session,
+ struct files_struct *files,
+ int fd, const char *filename,
+ unsigned int flags, fmode_t fmode),
+ TP_ARGS(session, files, fd, filename, flags, fmode));
+
+LTTNG_DEFINE_TRACE(lttng_statedump_start,
+ TP_PROTO(struct lttng_session *session),
+ TP_ARGS(session));
+
+LTTNG_DEFINE_TRACE(lttng_statedump_process_state,
+ TP_PROTO(struct lttng_session *session,
+ struct task_struct *p,
+ int type, int mode, int submode, int status,
+ struct files_struct *files),
+ TP_ARGS(session, p, type, mode, submode, status, files));
+
+LTTNG_DEFINE_TRACE(lttng_statedump_process_pid_ns,
+ TP_PROTO(struct lttng_session *session,
+ struct task_struct *p,
+ struct pid_namespace *pid_ns),
+ TP_ARGS(session, p, pid_ns));
+
+#if (LTTNG_LINUX_VERSION_CODE >= LTTNG_KERNEL_VERSION(4,6,0))
+LTTNG_DEFINE_TRACE(lttng_statedump_process_cgroup_ns,
+ TP_PROTO(struct lttng_session *session,
+ struct task_struct *p,
+ struct cgroup_namespace *cgroup_ns),
+ TP_ARGS(session, p, cgroup_ns));
#endif
-DEFINE_TRACE(lttng_statedump_process_ipc_ns);
+
+LTTNG_DEFINE_TRACE(lttng_statedump_process_ipc_ns,
+ TP_PROTO(struct lttng_session *session,
+ struct task_struct *p,
+ struct ipc_namespace *ipc_ns),
+ TP_ARGS(session, p, ipc_ns));
+
#ifndef LTTNG_MNT_NS_MISSING_HEADER
-DEFINE_TRACE(lttng_statedump_process_mnt_ns);
+LTTNG_DEFINE_TRACE(lttng_statedump_process_mnt_ns,
+ TP_PROTO(struct lttng_session *session,
+ struct task_struct *p,
+ struct mnt_namespace *mnt_ns),
+ TP_ARGS(session, p, 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);
+
+LTTNG_DEFINE_TRACE(lttng_statedump_process_net_ns,
+ TP_PROTO(struct lttng_session *session,
+ struct task_struct *p,
+ struct net *net_ns),
+ TP_ARGS(session, p, net_ns));
+
+LTTNG_DEFINE_TRACE(lttng_statedump_process_user_ns,
+ TP_PROTO(struct lttng_session *session,
+ struct task_struct *p,
+ struct user_namespace *user_ns),
+ TP_ARGS(session, p, user_ns));
+
+LTTNG_DEFINE_TRACE(lttng_statedump_process_uts_ns,
+ TP_PROTO(struct lttng_session *session,
+ struct task_struct *p,
+ struct uts_namespace *uts_ns),
+ TP_ARGS(session, p, uts_ns));
+
+LTTNG_DEFINE_TRACE(lttng_statedump_network_interface,
+ TP_PROTO(struct lttng_session *session,
+ struct net_device *dev, struct in_ifaddr *ifa),
+ TP_ARGS(session, dev, ifa));
+
#ifdef LTTNG_HAVE_STATEDUMP_CPU_TOPOLOGY
-DEFINE_TRACE(lttng_statedump_cpu_topology);
+LTTNG_DEFINE_TRACE(lttng_statedump_cpu_topology,
+ TP_PROTO(struct lttng_session *session, struct cpuinfo_x86 *c),
+ TP_ARGS(session, c));
#endif
struct lttng_fd_ctx {
char *page;
struct lttng_session *session;
- struct task_struct *p;
struct files_struct *files;
};
LTTNG_DEAD = 7,
};
+
+#if (LTTNG_LINUX_VERSION_CODE >= LTTNG_KERNEL_VERSION(5,11,0))
+
+#define LTTNG_PART_STRUCT_TYPE struct block_device
+
+static
+int lttng_get_part_name(struct gendisk *disk, struct block_device *part, char *name_buf)
+{
+ const char *p;
+
+ p = bdevname(part, name_buf);
+ if (!p)
+ return -ENOSYS;
+
+ return 0;
+}
+
+static
+dev_t lttng_get_part_devt(struct block_device *part)
+{
+ return part->bd_dev;
+}
+
+#else
+
+#define LTTNG_PART_STRUCT_TYPE struct hd_struct
+
+static
+int lttng_get_part_name(struct gendisk *disk, struct hd_struct *part, char *name_buf)
+{
+ const char *p;
+ struct block_device bdev;
+
+ /*
+ * Create a partial 'struct blockdevice' to use
+ * 'bdevname()' which is a simple wrapper over
+ * 'disk_name()' but has the honor to be EXPORT_SYMBOL.
+ */
+ bdev.bd_disk = disk;
+ bdev.bd_part = part;
+
+ p = bdevname(&bdev, name_buf);
+ if (!p)
+ return -ENOSYS;
+
+ return 0;
+}
+
+static
+dev_t lttng_get_part_devt(struct hd_struct *part)
+{
+ return part_devt(part);
+}
+#endif
+
static
int lttng_enumerate_block_devices(struct lttng_session *session)
{
while ((dev = class_dev_iter_next(&iter))) {
struct disk_part_iter piter;
struct gendisk *disk = dev_to_disk(dev);
- struct hd_struct *part;
+ LTTNG_PART_STRUCT_TYPE *part;
/*
* Don't show empty devices or things that have been
disk_part_iter_init(&piter, disk, DISK_PITER_INCL_PART0);
while ((part = disk_part_iter_next(&piter))) {
char name_buf[BDEVNAME_SIZE];
- char *p;
- p = wrapper_disk_name(disk, part->partno, name_buf);
- if (!p) {
+ if (lttng_get_part_name(disk, part, name_buf) == -ENOSYS) {
disk_part_iter_exit(&piter);
class_dev_iter_exit(&iter);
return -ENOSYS;
}
trace_lttng_statedump_block_device(session,
- part_devt(part), name_buf);
+ lttng_get_part_devt(part), name_buf);
}
disk_part_iter_exit(&piter);
}
/* 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 files_struct *files;
+ struct lttng_fd_ctx ctx = { .page = tmp, .session = session, .files = files, };
- task_lock(p);
- files = p->files;
- if (!files)
- goto end;
- ctx.files = files;
lttng_iterate_fd(files, 0, lttng_dump_one_fd, &ctx);
-end:
- task_unlock(p);
-}
-
-static
-int lttng_enumerate_file_descriptors(struct lttng_session *session)
-{
- struct task_struct *p;
- char *tmp;
-
- tmp = (char *) __get_free_page(GFP_KERNEL);
- if (!tmp)
- return -ENOMEM;
-
- /* 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);
- return 0;
}
#ifdef LTTNG_HAVE_STATEDUMP_CPU_TOPOLOGY
* "namespaces: Use task_lock and not rcu to protect nsproxy"
* for details.
*/
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0) || \
+#if (LTTNG_LINUX_VERSION_CODE >= LTTNG_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 = task_nsproxy(p);
#endif
if (proxy) {
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,6,0))
+#if (LTTNG_LINUX_VERSION_CODE >= LTTNG_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);
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) || \
+#if (LTTNG_LINUX_VERSION_CODE >= LTTNG_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))
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 =
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)
type = LTTNG_USER_THREAD;
else
type = LTTNG_KERNEL_THREAD;
+ files = p->files;
trace_lttng_statedump_process_state(session,
- p, type, mode, submode, status);
+ 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;
}
trace_lttng_statedump_start(session);
ret = lttng_enumerate_process_states(session);
- if (ret)
- return ret;
- ret = lttng_enumerate_file_descriptors(session);
if (ret)
return ret;
/*