--- /dev/null
+/*
+ * Copyright (C) 2011-2012 Mathieu Bain <mathieu.bain@polymtl.ca>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+extern "C"
+{
+#include <babeltrace/babeltrace.h>
+}
+
+#include "lttngtoptypes.h"
+#include "common.h"
+#include "iostreamtop.h"
+
+void add_file(Quark proc_quark, Quark file, int fd,
+ unsigned long timestamp)
+{
+ Quark parent;
+ Quark file_pointer;
+ Quark old_file;
+ std::string filename;
+ std::string old_filename;
+ std::string path;
+ int flag;
+
+ if (state_system->attributeExists(proc_quark, "threadparent")) {
+ parent = state_system->getQuark(proc_quark, "threadparent");
+ insert_file(parent, fd, timestamp);
+ }
+
+ path = path_name_from_fd(fd);
+ if (state_system->attributeExists(proc_quark, path + "/file")) {
+ file_pointer = state_system->getQuark(proc_quark, path);
+ get_current_attribute_value_quark(&file_pointer, "file",
+ old_file);
+ get_current_attribute_value_string(&old_file, "name",
+ old_filename);
+ get_current_attribute_value_string(&file, "name", filename);
+ if (old_filename != filename) {
+ /* Different file with same fd, we overwrite the
+ file pointer */
+ modify_attribute(timestamp, &file_pointer, "file",
+ file);
+ } else {
+ modify_attribute(timestamp, &old_file, "flag",
+ __NR_open);
+ }
+ } else {
+ file_pointer = state_system->getQuark(proc_quark, path);
+ modify_attribute(timestamp, &file_pointer, "file", file);
+
+ }
+ /* To easily retrieve file pointer from files_history */
+ modify_attribute(timestamp, &file, "fd", fd);
+
+ /* The file may have been created in the parent */
+ get_current_attribute_value_int(&file, "flag", flag);
+ if (flag == -1) {
+ modify_attribute(timestamp, &file, "fd", fd);
+ modify_attribute(timestamp, &file, "flag", __NR_open);
+ increment_attribute(timestamp, NULL, "nbfiles");
+ increment_attribute(timestamp, NULL, "nbnewfiles");
+ }
+}
+
+/*
+ * Edit the file
+ * Called by handled_statedump_filename
+ */
+void edit_file(unsigned long timestamp, Quark proc, Quark file, int fd)
+{
+ std::string path;
+
+ path = path_name_from_fd(fd);
+ if (!state_system->attributeExists(proc, path)) {
+ add_file(proc, file, fd, timestamp);
+ }
+}
+
+Quark create_file(Quark proc, std::string file_name, unsigned long timestamp)
+{
+ Quark file_history;
+ Quark old_newest;
+ bool has_old;
+ std::string current_path;
+ int index;
+ std::stringstream ss;
+
+ if (!get_current_attribute_value_quark(&proc, "files_history/current",
+ file_history)) {
+ /* First file for process */
+ file_history = state_system->getQuark(
+ proc, "files_history/file_0");
+ } else {
+
+ current_path = state_system->getFullAttributeName(file_history);
+ index = atoi(current_path.substr(current_path.find_last_of('_')
+ + 1).c_str());
+ ss << "files_history/file_" << index + 1;
+ current_path = ss.str();
+ file_history = state_system->getQuark(
+ proc, current_path);
+ }
+
+ has_old = get_current_attribute_value_quark(
+ &proc,"files_history/current", old_newest);
+ modify_attribute(timestamp, &proc, "files_history/current",
+ file_history);
+ modify_attribute(timestamp, &file_history, "file/name", file_name);
+ modify_attribute(timestamp, &file_history, "file/read", 0);
+ modify_attribute(timestamp, &file_history, "file/write", 0);
+ modify_attribute(timestamp, &file_history, "file/flag", -1);
+ modify_attribute(timestamp, &file_history, "file/fd", -1);
+ modify_attribute(timestamp, &file_history, "file/birth", timestamp);
+ if (has_old)
+ modify_attribute(timestamp, &file_history, "next", old_newest);
+ else
+ nullify_attribute(timestamp, &file_history, "next");
+
+ return state_system->getQuark(file_history, "file");
+}
+
+void insert_file(Quark proc, int fd, unsigned long timestamp)
+{
+ Quark file;
+ Quark parent;
+ Quark parent_file;
+ bool parent_has_file;
+ std::string name;
+
+ if (fd < 0)
+ return;
+
+ if (get_file(proc, fd, file)) {
+ if (state_system->attributeExists(proc, "threadparent")) {
+ parent = state_system->getQuark(proc, "threadparent");
+ parent_has_file = get_file(parent, fd,
+ parent_file);
+ if (parent_has_file) {
+ get_current_attribute_value_string(&parent_file,
+ "name",
+ name);
+ modify_attribute(timestamp, &file, "name",
+ name);
+ }
+ }
+ } else {
+ file = create_file(proc, "Unknown", timestamp);
+ add_file(proc, file, fd, timestamp);
+ }
+}
+
+void close_file(unsigned long timestamp, Quark proc, int fd)
+{
+ Quark file;
+ bool file_found;
+
+ file_found = get_current_attribute_value_quark(&proc,
+ path_name_from_fd(fd),
+ file);
+ if (file_found) {
+ modify_attribute(timestamp, &file, "flag", __NR_close);
+ decrement_attribute(timestamp, NULL, "nbfiles");
+ }
+ increment_attribute(timestamp, NULL, "nbdeadfiles");
+}
+
+bool get_file(Quark proc_quark, int fd, Quark &file_quark)
+{
+ std::string path = path_name_from_fd(fd);
+ Quark file_pointer;
+ if (state_system->attributeExists(proc_quark, path)) {
+ file_pointer = state_system->getQuark(proc_quark, path);
+ return get_current_attribute_value_quark(&file_pointer, "file",
+ file_quark);
+ } else {
+ return false;
+ }
+}
+
+void show_history(Quark proc)
+{
+ Quark file;
+ int fd;
+ std::string name;
+
+ if (get_current_attribute_value_quark(&proc, "files_history", file)) {
+ do {
+ fd = -1;
+ name = "";
+ get_current_attribute_value_int(&file, "file/fd", fd);
+ get_current_attribute_value_string(
+ &file, "file/name", name);
+ fprintf(stderr, "fd = %d, name = %s\n", fd,
+ name.c_str());
+ } while (get_current_attribute_value_quark(
+ &file, "next", file));
+ }
+
+}
+
+int update_iostream_ret(int tid, char *comm, unsigned long timestamp,
+ uint64_t cpu_id, int ret)
+{
+ Quark proc_quark;
+ Quark syscall_info_quark;
+ Quark file_quark;
+ Quark file_history_quark;
+ bool file_found;
+ int syscall_type;
+ int fd;
+ int err = 0;
+
+ proc_quark = get_proc(tid, comm, timestamp);
+
+ if (state_system->attributeExists(proc_quark, "syscall_info")) {
+ syscall_info_quark = state_system->getQuark(proc_quark,
+ "syscall_info");
+ get_current_attribute_value_int(&syscall_info_quark, "type",
+ syscall_type);
+ if (syscall_type == __NR_read && ret > 0) {
+ increase_attribute(timestamp, &proc_quark,
+ "totalfileread", ret);
+ increase_attribute(timestamp, &proc_quark,
+ "fileread", ret);
+ get_current_attribute_value_int(&syscall_info_quark,
+ "fd", fd);
+ file_found = get_file(proc_quark, fd,
+ file_quark);
+ if (file_found)
+ increase_attribute(timestamp, &file_quark,
+ "read", ret);
+ } else if (syscall_type == __NR_write && ret > 0) {
+
+ increase_attribute(timestamp, &proc_quark,
+ "totalfilewrite", ret);
+ increase_attribute(timestamp, &proc_quark,
+ "filewrite", ret);
+ get_current_attribute_value_int(&syscall_info_quark,
+ "fd", fd);
+ file_found = get_file(proc_quark, fd,
+ file_quark);
+ if (file_found)
+ increase_attribute(timestamp, &file_quark,
+ "write", ret);
+ } else if (syscall_type == __NR_open && ret > 0) {
+ file_history_quark = state_system->getQuark(proc_quark,
+ "files_history");
+ file_quark = state_system->getQuark(file_history_quark,
+ "current/file");
+ add_file(proc_quark, file_quark, ret,
+ timestamp);
+ modify_attribute(timestamp, &file_quark, "fd", fd);
+ } else {
+ err = -1;
+ }
+ }
+ return err;
+}
+
+void update_syscall_info(unsigned long timestamp, int type, int cpu_id,
+ Quark proc, int fd)
+{
+ int tid;
+
+ get_current_attribute_value_int(&proc, "tid", tid);
+ modify_attribute(timestamp, &proc, "syscall_info/type", type);
+ modify_attribute(timestamp, &proc, "syscall_info/cpu_id", cpu_id);
+ modify_attribute(timestamp, &proc, "syscall_info/tid", tid);
+ modify_attribute(timestamp, &proc, "syscall_info/fd", fd);
+}
+
+enum bt_cb_ret handle_exit_syscall(struct bt_ctf_event *call_data,
+ void *private_data)
+{
+ const struct definition *scope;
+ unsigned long timestamp;
+ char *comm;
+ uint64_t ret, tid;
+ uint64_t cpu_id;
+
+ timestamp = bt_ctf_get_timestamp(call_data);
+ if (timestamp == -1ULL)
+ goto error;
+
+ comm = get_context_comm(call_data);
+ tid = get_context_tid(call_data);
+
+ scope = bt_ctf_get_top_level_scope(call_data,
+ BT_EVENT_FIELDS);
+ ret = bt_ctf_get_int64(bt_ctf_get_field(call_data,
+ scope, "_ret"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing ret context info\n");
+ goto error;
+ }
+
+ cpu_id = get_cpu_id(call_data);
+
+ /*
+ * if we encounter an exit_syscall and
+ * it is not for a syscall read or write
+ * we just abort the execution of this callback
+ */
+ if (update_iostream_ret(
+ tid, comm, timestamp, cpu_id, ret) < 0)
+ return BT_CB_ERROR_CONTINUE;
+
+ return BT_CB_OK;
+
+error:
+ return BT_CB_ERROR_STOP;
+}
+
+enum bt_cb_ret handle_sys_write(struct bt_ctf_event *call_data,
+ void *private_data)
+{
+ const struct definition *scope;
+ Quark proc;
+ unsigned long timestamp;
+ int cpu_id;
+ int tid;
+ char *procname;
+ int fd;
+
+ timestamp = bt_ctf_get_timestamp(call_data);
+ if (timestamp == -1ULL)
+ goto error;
+
+ tid = get_context_tid(call_data);
+ cpu_id = get_cpu_id(call_data);
+
+ procname = get_context_comm(call_data);
+
+ scope = bt_ctf_get_top_level_scope(call_data,
+ BT_EVENT_FIELDS);
+ fd = bt_ctf_get_uint64(bt_ctf_get_field(call_data,
+ scope, "_fd"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing fd context info\n");
+ goto error;
+ }
+
+ proc = get_proc(tid, procname, timestamp);
+ update_syscall_info(timestamp, __NR_write, cpu_id, proc, fd);
+
+ insert_file(proc, fd, timestamp);
+
+ return BT_CB_OK;
+
+error:
+ return BT_CB_ERROR_STOP;
+}
+
+enum bt_cb_ret handle_sys_read(struct bt_ctf_event *call_data,
+ void *private_data)
+{
+ const struct definition *scope;
+ Quark proc;
+ unsigned long timestamp;
+ uint64_t cpu_id;
+ int64_t tid;
+ char *procname;
+ int fd;
+
+ timestamp = bt_ctf_get_timestamp(call_data);
+ if (timestamp == -1ULL)
+ goto error;
+
+ tid = get_context_tid(call_data);
+ cpu_id = get_cpu_id(call_data);
+
+ procname = get_context_comm(call_data);
+
+ scope = bt_ctf_get_top_level_scope(call_data,
+ BT_EVENT_FIELDS);
+ fd = bt_ctf_get_uint64(bt_ctf_get_field(call_data,
+ scope, "_fd"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing fd context info\n");
+ goto error;
+ }
+
+ proc = get_proc(tid, procname, timestamp);
+ update_syscall_info(timestamp, __NR_read, cpu_id, proc, fd);
+
+ insert_file(proc, fd, timestamp);
+
+ return BT_CB_OK;
+
+error:
+ return BT_CB_ERROR_STOP;
+}
+
+enum bt_cb_ret handle_sys_open(struct bt_ctf_event *call_data,
+ void *private_data)
+{
+ Quark proc;
+ const struct definition *scope;
+ unsigned long timestamp;
+ uint64_t cpu_id;
+ int64_t tid;
+ char *procname;
+ char *file;
+
+ timestamp = bt_ctf_get_timestamp(call_data);
+ if (timestamp == -1ULL)
+ goto error;
+
+ tid = get_context_tid(call_data);
+ cpu_id = get_cpu_id(call_data);
+
+ procname = get_context_comm(call_data);
+
+ scope = bt_ctf_get_top_level_scope(call_data,
+ BT_EVENT_FIELDS);
+ file = bt_ctf_get_string(bt_ctf_get_field(call_data,
+ scope, "_filename"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing file name context info\n");
+ goto error;
+ }
+
+ proc = get_proc(tid, procname, timestamp);
+ update_syscall_info(timestamp, __NR_open, cpu_id, proc, -1);
+
+ create_file(proc, file, timestamp);
+
+ return BT_CB_OK;
+
+error:
+ return BT_CB_ERROR_STOP;
+}
+
+enum bt_cb_ret handle_sys_close(struct bt_ctf_event *call_data,
+ void *private_data)
+{
+ const struct definition *scope;
+ Quark proc;
+ unsigned long timestamp;
+ int64_t tid;
+ char *procname;
+ int fd;
+
+ timestamp = bt_ctf_get_timestamp(call_data);
+ if (timestamp == -1ULL)
+ goto error;
+
+ tid = get_context_tid(call_data);
+
+ procname = get_context_comm(call_data);
+
+ scope = bt_ctf_get_top_level_scope(call_data,
+ BT_EVENT_FIELDS);
+ fd = bt_ctf_get_uint64(bt_ctf_get_field(call_data,
+ scope, "_fd"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing fd context info\n");
+ goto error;
+ }
+
+ proc = get_proc(tid, procname, timestamp);
+
+ close_file(timestamp, proc, fd);
+
+ return BT_CB_OK;
+
+error:
+ return BT_CB_ERROR_STOP;
+}
+
+enum bt_cb_ret handle_statedump_file_descriptor(
+ struct bt_ctf_event *call_data, void *private_data)
+{
+ const struct definition *scope;
+ Quark parent;
+ Quark file;
+ unsigned long timestamp;
+ int64_t pid;
+ char *file_name;
+ int fd;
+
+ timestamp = bt_ctf_get_timestamp(call_data);
+ if (timestamp == -1ULL)
+ goto error;
+
+ scope = bt_ctf_get_top_level_scope(call_data,
+ BT_EVENT_FIELDS);
+ pid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
+ scope, "_pid"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing tid context info\n");
+ goto error;
+ }
+
+ scope = bt_ctf_get_top_level_scope(call_data,
+ BT_EVENT_FIELDS);
+ fd = bt_ctf_get_int64(bt_ctf_get_field(call_data,
+ scope, "_fd"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing fd context info\n");
+ goto error;
+ }
+
+ scope = bt_ctf_get_top_level_scope(call_data,
+ BT_EVENT_FIELDS);
+ file_name = bt_ctf_get_string(bt_ctf_get_field(call_data,
+ scope, "_filename"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing file name context info\n");
+ goto error;
+ }
+
+ parent = get_proc_pid(pid, pid, timestamp);
+ create_file(parent, file_name, timestamp);
+ file = state_system->getQuark(parent, "files_history/current/file");
+ edit_file(timestamp, parent, file, fd);
+
+ return BT_CB_OK;
+
+error:
+ return BT_CB_ERROR_STOP;
+}