Currently the LTTng-modules statedump simply iterates over all processes
in the system and assumes all threads share the same file descriptor
table, which is only true if threads were created with clone
CLONE_FILES.
Directly invoking clone without the CLONE_FILES creates threads which
belong to the same process, but have their own file descriptor table.
Therefore, model-wise, we cannot assume that all threads in a process
have the same fd table content.
Add a new "file_table_address" field to the lttng_statedump_process_state
event, which dumps the address of the thread's struct files_struct
pointer. This pointer is guaranteed to never be re-used while we hold
the RCU read-side lock (so for the entire iteration over
processes/threads).
For the lttng_statedump_file_descriptor event, remove the "pid" field
(which is semantically inaccurate) and add a "file_table_address" field,
which contains the struct files_struct address of the file table
containing the file descriptor.
An optimization is performed to eliminate most duplcated file table
content by skipping file table dump if the same file table address is
encountered consecutively while iterating over a process' threads.
This introduces a semantic change to the statedump fields, and will
therefore be introduced in lttng-modules 2.12 onwards, not backported as
a fix.
Fixes: #1245
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
LTTNG_TRACEPOINT_EVENT(lttng_statedump_process_state,
TP_PROTO(struct lttng_session *session,
struct task_struct *p,
LTTNG_TRACEPOINT_EVENT(lttng_statedump_process_state,
TP_PROTO(struct lttng_session *session,
struct task_struct *p,
- int type, int mode, int submode, int status),
- TP_ARGS(session, p, type, mode, submode, status),
+ int type, int mode, int submode, int status,
+ struct files_struct *files),
+ TP_ARGS(session, p, type, mode, submode, status, files),
TP_FIELDS(
ctf_integer(pid_t, tid, p->pid)
ctf_integer(pid_t, pid, p->tgid)
TP_FIELDS(
ctf_integer(pid_t, tid, p->pid)
ctf_integer(pid_t, pid, p->tgid)
ctf_integer(int, submode, submode)
ctf_integer(int, status, status)
ctf_integer(unsigned int, cpu, task_cpu(p))
ctf_integer(int, submode, submode)
ctf_integer(int, status, status)
ctf_integer(unsigned int, cpu, task_cpu(p))
+ ctf_integer_hex(struct files_struct *, file_table_address, files)
LTTNG_TRACEPOINT_EVENT(lttng_statedump_file_descriptor,
TP_PROTO(struct lttng_session *session,
LTTNG_TRACEPOINT_EVENT(lttng_statedump_file_descriptor,
TP_PROTO(struct lttng_session *session,
- struct task_struct *p, int fd, const char *filename,
+ struct files_struct *files,
+ int fd, const char *filename,
unsigned int flags, fmode_t fmode),
unsigned int flags, fmode_t fmode),
- TP_ARGS(session, p, fd, filename, flags, fmode),
+ TP_ARGS(session, files, fd, filename, flags, fmode),
- ctf_integer(pid_t, pid, p->tgid)
+ ctf_integer_hex(struct files_struct *, file_table_address, files)
ctf_integer(int, fd, fd)
ctf_integer_oct(unsigned int, flags, flags)
ctf_integer_hex(fmode_t, fmode, fmode)
ctf_integer(int, fd, fd)
ctf_integer_oct(unsigned int, flags, flags)
ctf_integer_hex(fmode_t, fmode, fmode)
struct lttng_fd_ctx {
char *page;
struct lttng_session *session;
struct lttng_fd_ctx {
char *page;
struct lttng_session *session;
struct files_struct *files;
};
struct files_struct *files;
};
/* Make sure we give at least some info */
spin_lock(&dentry->d_lock);
/* 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;
}
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);
+/* Called with task lock held. */
-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);
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
}
#ifdef LTTNG_HAVE_STATEDUMP_CPU_TOPOLOGY
int lttng_enumerate_process_states(struct lttng_session *session)
{
struct task_struct *g, *p;
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) {
rcu_read_lock();
for_each_process(g) {
+ struct files_struct *prev_files = NULL;
+
p = g;
do {
enum lttng_execution_mode mode =
p = g;
do {
enum lttng_execution_mode mode =
LTTNG_UNKNOWN;
enum lttng_process_status status;
enum lttng_thread_type type;
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)
task_lock(p);
if (p->exit_state == EXIT_ZOMBIE)
type = LTTNG_USER_THREAD;
else
type = LTTNG_KERNEL_THREAD;
type = LTTNG_USER_THREAD;
else
type = LTTNG_KERNEL_THREAD;
trace_lttng_statedump_process_state(session,
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);
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();
task_unlock(p);
} while_each_thread(g, p);
}
rcu_read_unlock();
+ free_page((unsigned long) tmp);
+
trace_lttng_statedump_start(session);
ret = lttng_enumerate_process_states(session);
trace_lttng_statedump_start(session);
ret = lttng_enumerate_process_states(session);
- if (ret)
- return ret;
- ret = lttng_enumerate_file_descriptors(session);