From patchwork Wed Aug 28 11:24:55 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Tzvetomir Stoyanov (VMware)" X-Patchwork-Id: 11118723 X-Patchwork-Delegate: rostedt@goodmis.org Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id AF76114F7 for ; Wed, 28 Aug 2019 11:25:04 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 856DB22CF8 for ; Wed, 28 Aug 2019 11:25:04 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="ZVqWp/BA" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726270AbfH1LZE (ORCPT ); Wed, 28 Aug 2019 07:25:04 -0400 Received: from mail-wm1-f68.google.com ([209.85.128.68]:36821 "EHLO mail-wm1-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726408AbfH1LZE (ORCPT ); Wed, 28 Aug 2019 07:25:04 -0400 Received: by mail-wm1-f68.google.com with SMTP id p13so2233260wmh.1 for ; Wed, 28 Aug 2019 04:25:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=NdOzju8h45Z5YBOmkFuoCNJV38KV0/qaAbJ+IxsLHV0=; b=ZVqWp/BAUg0O0GdF5f6FtscFO6kM3ZpHL3FPCvsksNBlSOmqbqmaprYYuMmR5nwvSy A8B6HQ0lQbjs2nIDxJnql3F60yfmwl4qzS0qW/xBPYNpf4GiSzrA/YrB67Qeuw4N3jOh 0nCGzcw7XQhU5d/R1jXcoV43n/e7XHzOlrHKrEWV7P5euePbERYErDZGtNrEsNLArpdn oTRqvtK6VHL8j6LRXOieBBYS87CZd1tdzlQ7r1vpKBysQorHEuq+byEvsFB5O4PQHTYL Nzf6oXkoOaBL+HDQwZTIDwpu9ppt7Q+/e8K1kh5DlmrolXJDYKJJub0nJgbql2CwLv8j fHbw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=NdOzju8h45Z5YBOmkFuoCNJV38KV0/qaAbJ+IxsLHV0=; b=TT/qMDXX60FgEnxhCYU8Z5wtHNk6mvXha9cM+OG92nl+Ulx/nnPBb6VLJfqWopiPAi ffOLaH52JOgca5YldG4zLQQXTifveYwf7/3s/WFm9q77eHAswooX+5wlNyTSkoCQJ+/g i2Eikv97NdW7LkGP1h7Qj9pqLE+vOuMTX4XYBRl9t2IoFRrc9D/jIRO1aVagvxO2+UWj 1Q0OL0zZY+M0jXq0efnpFOXOtCQdewbP+rPaM0mj+/PHi+cQKWTThTm75wBOzZFwnBx7 TOJcC3q+iCL9hEDZRemHnHBkPuGe1PKQ9Inr7eAMorKW6GHStNpa6EBrfnVecPKQueaa 7ABg== X-Gm-Message-State: APjAAAV3X71APXiYtzWjTUQ5wjuSD6StsCJqVnykbDSMJK1GNmqEdlBZ zngWRCNL8ffnZtrkn+MNqPU= X-Google-Smtp-Source: APXvYqwhcy1jRSANU263LkpfrO1CdzjoRTFgYWNikxwny4LRXsPG6P67DqlVxXLKL2t3z86AjPjiqg== X-Received: by 2002:a1c:4c04:: with SMTP id z4mr4253891wmf.1.1566991501863; Wed, 28 Aug 2019 04:25:01 -0700 (PDT) Received: from oberon.eng.vmware.com ([146.247.46.5]) by smtp.gmail.com with ESMTPSA id c132sm2846766wme.27.2019.08.28.04.25.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 28 Aug 2019 04:25:00 -0700 (PDT) From: "Tzvetomir Stoyanov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org Subject: [PATCH v6 1/3] trace-cmd: Extend ptrace logic to work with multiple filtered pids Date: Wed, 28 Aug 2019 14:24:55 +0300 Message-Id: <20190828112457.10533-2-tz.stoyanov@gmail.com> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190828112457.10533-1-tz.stoyanov@gmail.com> References: <20190828112457.10533-1-tz.stoyanov@gmail.com> MIME-Version: 1.0 Sender: linux-trace-devel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org In the trace-record.c file there is a global variable named "filter_pid". It is not set anywhere in the code, but there is a logic which relies on it. This variable is a leftover from the past implementation of trace-cmd record "-P" option, when it was designed to filter only a single PID. Now "-P" option works with a list of PIDs, stored in filter_pids global list. The code is modified to work with filter_pids instead of filter_pid. This logic is used only if there is no ftrace "options/event-fork" on the system and we have ptrace support. There is one significant change in the trace-cmd record behavior in this specific use case: - filtered pids are specified with the "-P" option. - there is no ftrace "options/event-fork" on the system. - there is ptrace support. The trace continues until Ctrl^C is hit or all filtered PIDs exit, whatever comes first. Signed-off-by: Tzvetomir Stoyanov (VMware) --- tracecmd/trace-record.c | 69 +++++++++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/tracecmd/trace-record.c b/tracecmd/trace-record.c index 5dc6f17..917f52a 100644 --- a/tracecmd/trace-record.c +++ b/tracecmd/trace-record.c @@ -86,7 +86,6 @@ static bool use_tcp; static int do_ptrace; static int filter_task; -static int filter_pid = -1; static bool no_filter = false; static int local_cpu_count; @@ -866,6 +865,13 @@ static void add_filter_pid(int pid, int exclude) struct filter_pids *p; char buf[100]; + for (p = filter_pids; p; p = p->next) { + if (p->pid == pid) { + p->exclude = exclude; + return; + } + } + p = malloc(sizeof(*p)); if (!p) die("Failed to allocate pid filter"); @@ -1223,17 +1229,36 @@ static void enable_ptrace(void) ptrace(PTRACE_TRACEME, 0, NULL, 0); } -static void ptrace_wait(enum trace_type type, int main_pid) +static void ptrace_wait(enum trace_type type) { + struct filter_pids *fpid; unsigned long send_sig; unsigned long child; siginfo_t sig; + int main_pids; int cstatus; int status; + int i = 0; + int *pids; int event; int pid; int ret; + pids = calloc(nr_filter_pids, sizeof(int)); + if (!pids) { + warning("Unable to allocate array for %d PIDs", nr_filter_pids); + return; + } + + for (fpid = filter_pids; fpid; fpid = fpid->next) { + if (fpid->exclude) + continue; + pids[i++] = fpid->pid; + if (i >= nr_filter_pids) + break; + } + main_pids = i; + do { ret = trace_waitpid(type, -1, &status, WSTOPPED | __WALL); if (ret < 0) @@ -1275,11 +1300,24 @@ static void ptrace_wait(enum trace_type type, int main_pid) PTRACE_O_TRACEEXIT); ptrace(PTRACE_CONT, pid, NULL, send_sig); } - } while (!finished && ret > 0 && - (!WIFEXITED(status) || pid != main_pid)); + if (WIFEXITED(status) || + (WIFSTOPPED(status) && event == PTRACE_EVENT_EXIT)) { + for (i = 0; i < nr_filter_pids; i++) { + if (pid == pids[i]) { + pids[i] = 0; + main_pids--; + if (!main_pids) + finished = 1; + break; + } + } + } + } while (!finished && ret > 0); + + free(pids); } #else -static inline void ptrace_wait(enum trace_type type, int main_pid) { } +static inline void ptrace_wait(enum trace_type type) { } static inline void enable_ptrace(void) { } static inline void ptrace_attach(int pid) { } @@ -1289,8 +1327,8 @@ static void trace_or_sleep(enum trace_type type) { struct timeval tv = { 1 , 0 }; - if (do_ptrace && filter_pid >= 0) - ptrace_wait(type, filter_pid); + if (do_ptrace && filter_pids) + ptrace_wait(type); else if (type & TRACE_TYPE_STREAM) trace_stream_read(pids, recorder_threads, &tv); else @@ -1327,7 +1365,7 @@ static void run_cmd(enum trace_type type, int argc, char **argv) } if (do_ptrace) { add_filter_pid(pid, 0); - ptrace_wait(type, pid); + ptrace_wait(type); } else trace_waitpid(type, pid, &status, 0); } @@ -2318,7 +2356,7 @@ create_event(struct buffer_instance *instance, char *path, struct event_list *ol *event = *old_event; add_event(instance, event); - if (event->filter || filter_task || filter_pid) { + if (event->filter || filter_task || filter_pids) { event->filter_file = strdup(path); if (!event->filter_file) die("malloc filter file"); @@ -4924,9 +4962,9 @@ static void parse_record_options(int argc, add_func(&ctx->instance->filter_funcs, ctx->instance->filter_mod, "*"); - if (do_ptrace && !filter_task && (filter_pid < 0)) + if (do_ptrace && !filter_task && !nr_filter_pids) die(" -c can only be used with -F (or -P with event-fork support)"); - if (ctx->do_child && !filter_task &&! filter_pid) + if (ctx->do_child && !filter_task && !nr_filter_pids) die(" -c can only be used with -P or -F"); if ((argc - optind) >= 2) { @@ -4997,6 +5035,7 @@ static void record_trace(int argc, char **argv, { enum trace_type type = get_trace_cmd_type(ctx->curr_cmd); struct buffer_instance *instance; + struct filter_pids *pid; /* * If top_instance doesn't have any plugins or events, then @@ -5083,8 +5122,12 @@ static void record_trace(int argc, char **argv, update_task_filter(); tracecmd_enable_tracing(); /* We don't ptrace ourself */ - if (do_ptrace && filter_pid >= 0) - ptrace_attach(filter_pid); + if (do_ptrace && filter_pids) { + for (pid = filter_pids; pid; pid = pid->next) { + if (!pid->exclude) + ptrace_attach(pid->pid); + } + } /* sleep till we are woken with Ctrl^C */ printf("Hit Ctrl^C to stop recording\n"); while (!finished) From patchwork Wed Aug 28 11:24:56 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Tzvetomir Stoyanov (VMware)" X-Patchwork-Id: 11118729 X-Patchwork-Delegate: rostedt@goodmis.org Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5F377184E for ; Wed, 28 Aug 2019 11:25:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 2A0442173E for ; Wed, 28 Aug 2019 11:25:08 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="fFsBY1+o" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726315AbfH1LZI (ORCPT ); Wed, 28 Aug 2019 07:25:08 -0400 Received: from mail-wm1-f66.google.com ([209.85.128.66]:50262 "EHLO mail-wm1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726382AbfH1LZH (ORCPT ); Wed, 28 Aug 2019 07:25:07 -0400 Received: by mail-wm1-f66.google.com with SMTP id v15so2389969wml.0 for ; Wed, 28 Aug 2019 04:25:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=QQqVnTclKbDAEybIuELqvFcl3joOdQv073YX2TQr4cA=; b=fFsBY1+oTOjoJT2pyHPO2zuCWk0HvkTGkXYb8yYTA25TR0fkKE3GhUZ7pfnnQmqyBy HhYF9RJ/tcGVOIbKhabPE1uoJbGkVZzCv3qfKreiyqWZj5yRdIWD6WBpt9ZHPp3zMm2H KqcGtUMM6rxCaoUoEwYBpmwH4oHssRQTM3qaGZ1wXZEkTDPMCNEnikcNhxnCOsEoG/v7 xX6k8vSduphdyg23X0TtsnIbYE4jamCUXytPA12AxDQ7YbLGHBXf4lvUd7YaLIeAmDUw Nwqk4ZUldfII11iClyQtliuM5YYEuhwF9d0igDRpvIhZUvug0AHneKChvDXuX9x3v8A5 OWcQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=QQqVnTclKbDAEybIuELqvFcl3joOdQv073YX2TQr4cA=; b=PdBfriQoXo9Im/RFbjaKPjtUWigu75KuJ5S7ulGdQsRNFKiiAQTyTzw2kBQlzKxSkY wgPggT7swq65bpsJM4KBV8J/mUbHdAKuRBcK/hjyDO0D7JzPqRtonU7bcpqTVsrr61hh /W8YXZHsnrYbY62yOg3oWlJTJXUNk2wcm2lnP+xix0i//K8vEKqYY+VLQIA7hnjy4GrE /N2yK5MPAWV0NZFwQ1GKVl95leVQgccxSB3A3bxFM1QfNFESNqwm1vkAsxLuy6fZBnOy C46J58loA+exUD5gJYgXjgzXi48swS8TGIywvlEj5oePi4UE351MTei7gi8xrNCrTUT1 fdtQ== X-Gm-Message-State: APjAAAU31Q07ga/9eU0Jq7nHx9oUTFlFSHfoKqEa/2Ex5yrx6nNsWeiv HiI/mVyJNyEnyrCcdzdgkc4= X-Google-Smtp-Source: APXvYqyJRcNWqR72pD0/3HPdr/iibBbvKaJnPw8VLrkaOa3S20ZkQop+M+1kZUfNil8KKLvK0lMMVQ== X-Received: by 2002:a05:600c:21d3:: with SMTP id x19mr4158570wmj.45.1566991502981; Wed, 28 Aug 2019 04:25:02 -0700 (PDT) Received: from oberon.eng.vmware.com ([146.247.46.5]) by smtp.gmail.com with ESMTPSA id c132sm2846766wme.27.2019.08.28.04.25.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 28 Aug 2019 04:25:02 -0700 (PDT) From: "Tzvetomir Stoyanov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org Subject: [PATCH v6 2/3] trace-cmd: Save the tracee address map into the trace.dat file. Date: Wed, 28 Aug 2019 14:24:56 +0300 Message-Id: <20190828112457.10533-3-tz.stoyanov@gmail.com> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190828112457.10533-1-tz.stoyanov@gmail.com> References: <20190828112457.10533-1-tz.stoyanov@gmail.com> MIME-Version: 1.0 Sender: linux-trace-devel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org A new trace-cmd record option is added: "--proc-map". When it is set the address map of the traced applications is stored in the trace.dat file. The traced applications can be specified using the option -P, or as a given 'command'. A new API tracecmd_search_task_map() can be used to look up into stored address maps. The map is retrieved from /proc//maps file. Signed-off-by: Tzvetomir Stoyanov (VMware) --- Documentation/trace-cmd-record.1.txt | 3 + include/trace-cmd/trace-cmd.h | 10 ++ lib/trace-cmd/trace-input.c | 172 ++++++++++++++++++++++++++- tracecmd/include/trace-local.h | 10 ++ tracecmd/trace-record.c | 172 ++++++++++++++++++++++++++- tracecmd/trace-usage.c | 1 + 6 files changed, 364 insertions(+), 4 deletions(-) diff --git a/Documentation/trace-cmd-record.1.txt b/Documentation/trace-cmd-record.1.txt index 26a8299..e697f03 100644 --- a/Documentation/trace-cmd-record.1.txt +++ b/Documentation/trace-cmd-record.1.txt @@ -288,6 +288,9 @@ OPTIONS '--module snd -n "*"' is equivalent to '-n :mod:snd' +*--proc-map*:: + Save the traced process address map into the trace.dat file. The traced + processes can be specified using the option *-P*, or as a given 'command'. *--profile*:: With the *--profile* option, "trace-cmd" will enable tracing that can diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h index 6f62ab9..c4a437a 100644 --- a/include/trace-cmd/trace-cmd.h +++ b/include/trace-cmd/trace-cmd.h @@ -82,6 +82,7 @@ enum { TRACECMD_OPTION_OFFSET, TRACECMD_OPTION_CPUCOUNT, TRACECMD_OPTION_VERSION, + TRACECMD_OPTION_PROCMAPS, }; enum { @@ -97,6 +98,12 @@ struct tracecmd_ftrace { int long_size; }; +struct tracecmd_proc_addr_map { + unsigned long long start; + unsigned long long end; + char *lib_name; +}; + typedef void (*tracecmd_show_data_func)(struct tracecmd_input *handle, struct tep_record *record); typedef void (*tracecmd_handle_init_func)(struct tracecmd_input *handle, @@ -208,6 +215,9 @@ unsigned long long tracecmd_page_ts(struct tracecmd_input *handle, unsigned int tracecmd_record_ts_delta(struct tracecmd_input *handle, struct tep_record *record); +struct tracecmd_proc_addr_map * +tracecmd_search_task_map(struct tracecmd_input *handle, + int pid, unsigned long long addr); #ifndef SWIG /* hack for function graph work around */ extern __thread struct tracecmd_input *tracecmd_curr_thread_handle; diff --git a/lib/trace-cmd/trace-input.c b/lib/trace-cmd/trace-input.c index 654101f..a6fa7f5 100644 --- a/lib/trace-cmd/trace-input.c +++ b/lib/trace-cmd/trace-input.c @@ -101,6 +101,7 @@ struct tracecmd_input { struct tracecmd_ftrace finfo; struct hook_list *hooks; + struct pid_addr_maps *pid_maps; /* file information */ size_t header_files_start; size_t ftrace_files_start; @@ -2136,6 +2137,167 @@ void tracecmd_set_ts2secs(struct tracecmd_input *handle, handle->use_trace_clock = false; } +static int trace_pid_map_cmp(const void *a, const void *b) +{ + struct tracecmd_proc_addr_map *m_a = (struct tracecmd_proc_addr_map *)a; + struct tracecmd_proc_addr_map *m_b = (struct tracecmd_proc_addr_map *)b; + + if (m_a->start > m_b->start) + return 1; + if (m_a->start < m_b->start) + return -1; + return 0; +} + +static void procmap_free(struct pid_addr_maps *maps) +{ + int i; + + if (!maps) + return; + if (maps->lib_maps) { + for (i = 0; i < maps->nr_lib_maps; i++) + free(maps->lib_maps[i].lib_name); + free(maps->lib_maps); + } + free(maps->proc_name); + free(maps); +} + +#define STR_PROCMAP_LINE_MAX (PATH_MAX+22) +static int trace_pid_map_load(struct tracecmd_input *handle, char *buf) +{ + struct pid_addr_maps *maps = NULL; + char mapname[STR_PROCMAP_LINE_MAX]; + char *line; + int res; + int ret; + int i; + + maps = calloc(1, sizeof(*maps)); + if (!maps) + return -ENOMEM; + + ret = -EINVAL; + line = strchr(buf, '\n'); + if (!line) + goto out_fail; + + *line = '\0'; + if (strlen(buf) > STR_PROCMAP_LINE_MAX) + goto out_fail; + + res = sscanf(buf, "%x %x %s", &maps->pid, &maps->nr_lib_maps, mapname); + if (res != 3) + goto out_fail; + + ret = -ENOMEM; + maps->proc_name = strdup(mapname); + if (!maps->proc_name) + goto out_fail; + + maps->lib_maps = calloc(maps->nr_lib_maps, sizeof(struct tracecmd_proc_addr_map)); + if (!maps->lib_maps) + goto out_fail; + + buf = line + 1; + line = strchr(buf, '\n'); + for (i = 0; i < maps->nr_lib_maps; i++) { + if (!line) + break; + *line = '\0'; + if (strlen(buf) > STR_PROCMAP_LINE_MAX) + break; + res = sscanf(buf, "%llx %llx %s", &maps->lib_maps[i].start, + &maps->lib_maps[i].end, mapname); + if (res != 3) + break; + maps->lib_maps[i].lib_name = strdup(mapname); + if (!maps->lib_maps[i].lib_name) + goto out_fail; + buf = line + 1; + line = strchr(buf, '\n'); + } + + ret = -EINVAL; + if (i != maps->nr_lib_maps) + goto out_fail; + + qsort(maps->lib_maps, maps->nr_lib_maps, + sizeof(*maps->lib_maps), trace_pid_map_cmp); + + maps->next = handle->pid_maps; + handle->pid_maps = maps; + + return 0; + +out_fail: + procmap_free(maps); + return ret; +} + +static void trace_pid_map_free(struct pid_addr_maps *maps) +{ + struct pid_addr_maps *del; + + while (maps) { + del = maps; + maps = maps->next; + procmap_free(del); + } +} + +static int trace_pid_map_search(const void *a, const void *b) +{ + struct tracecmd_proc_addr_map *key = (struct tracecmd_proc_addr_map *)a; + struct tracecmd_proc_addr_map *map = (struct tracecmd_proc_addr_map *)b; + + if (key->start >= map->end) + return 1; + if (key->start < map->start) + return -1; + return 0; +} + +/** + * tracecmd_search_task_map - Search task memory address map + * @handle: input handle to the trace.dat file + * @pid: pid of the task + * @addr: address from the task memory space. + * + * Map of the task memory can be saved in the trace.dat file, using the option + * "--proc-map". If there is such information, this API can be used to look up + * into this memory map to find what library is loaded at the given @addr. + * + * A pointer to struct tracecmd_proc_addr_map is returned, containing the name + * of the library at given task @addr and the library start and end addresses. + */ +struct tracecmd_proc_addr_map * +tracecmd_search_task_map(struct tracecmd_input *handle, + int pid, unsigned long long addr) +{ + struct tracecmd_proc_addr_map *lib; + struct tracecmd_proc_addr_map key; + struct pid_addr_maps *maps; + + if (!handle || !handle->pid_maps) + return NULL; + + maps = handle->pid_maps; + while (maps) { + if (maps->pid == pid) + break; + maps = maps->next; + } + if (!maps || !maps->nr_lib_maps || !maps->lib_maps) + return NULL; + key.start = addr; + lib = bsearch(&key, maps->lib_maps, maps->nr_lib_maps, + sizeof(*maps->lib_maps), trace_pid_map_search); + + return lib; +} + static int handle_options(struct tracecmd_input *handle) { unsigned long long offset; @@ -2223,9 +2385,6 @@ static int handle_options(struct tracecmd_input *handle) case TRACECMD_OPTION_UNAME: handle->uname = strdup(buf); break; - case TRACECMD_OPTION_VERSION: - handle->version = strdup(buf); - break; case TRACECMD_OPTION_HOOK: hook = tracecmd_create_event_hook(buf); hook->next = handle->hooks; @@ -2235,6 +2394,10 @@ static int handle_options(struct tracecmd_input *handle) cpus = *(int *)buf; handle->cpus = tep_read_number(handle->pevent, &cpus, 4); break; + case TRACECMD_OPTION_PROCMAPS: + if (buf[size-1] == '\0') + trace_pid_map_load(handle, buf); + break; default: warning("unknown option %d", option); break; @@ -2848,6 +3011,9 @@ void tracecmd_close(struct tracecmd_input *handle) tracecmd_free_hooks(handle->hooks); handle->hooks = NULL; + trace_pid_map_free(handle->pid_maps); + handle->pid_maps = NULL; + if (handle->flags & TRACECMD_FL_BUFFER_INSTANCE) tracecmd_close(handle->parent); else { diff --git a/tracecmd/include/trace-local.h b/tracecmd/include/trace-local.h index 1cad3cc..78c52dc 100644 --- a/tracecmd/include/trace-local.h +++ b/tracecmd/include/trace-local.h @@ -157,6 +157,14 @@ struct func_list { const char *mod; }; +struct pid_addr_maps { + struct pid_addr_maps *next; + struct tracecmd_proc_addr_map *lib_maps; + unsigned int nr_lib_maps; + char *proc_name; + int pid; +}; + struct buffer_instance { struct buffer_instance *next; const char *name; @@ -183,6 +191,8 @@ struct buffer_instance { struct tracecmd_msg_handle *msg_handle; struct tracecmd_output *network_handle; + struct pid_addr_maps *pid_maps; + char *max_graph_depth; int flags; diff --git a/tracecmd/trace-record.c b/tracecmd/trace-record.c index 917f52a..4a20339 100644 --- a/tracecmd/trace-record.c +++ b/tracecmd/trace-record.c @@ -84,6 +84,8 @@ static int max_kb; static bool use_tcp; static int do_ptrace; +static int do_children; +static int get_procmap; static int filter_task; static bool no_filter = false; @@ -1068,6 +1070,121 @@ static char *make_pid_filter(char *curr_filter, const char *field) return filter; } +#define _STRINGIFY(x) #x +#define STRINGIFY(x) _STRINGIFY(x) + +static int get_pid_addr_maps(int pid) +{ + struct buffer_instance *instance = &top_instance; + struct pid_addr_maps *maps = instance->pid_maps; + struct tracecmd_proc_addr_map *map; + unsigned long long begin, end; + struct pid_addr_maps *m; + char mapname[PATH_MAX+1]; + char fname[PATH_MAX+1]; + char buf[PATH_MAX+100]; + FILE *f; + int ret; + int res; + int i; + + sprintf(fname, "/proc/%d/exe", pid); + ret = readlink(fname, mapname, PATH_MAX); + if (ret >= PATH_MAX || ret < 0) + return -ENOENT; + mapname[ret] = 0; + + sprintf(fname, "/proc/%d/maps", pid); + f = fopen(fname, "r"); + if (!f) + return -ENOENT; + + while (maps) { + if (pid == maps->pid) + break; + maps = maps->next; + } + + ret = -ENOMEM; + if (!maps) { + maps = calloc(1, sizeof(*maps)); + if (!maps) + goto out_fail; + maps->pid = pid; + maps->next = instance->pid_maps; + instance->pid_maps = maps; + } else { + for (i = 0; i < maps->nr_lib_maps; i++) + free(maps->lib_maps[i].lib_name); + free(maps->lib_maps); + maps->lib_maps = NULL; + maps->nr_lib_maps = 0; + free(maps->proc_name); + } + + maps->proc_name = strdup(mapname); + if (!maps->proc_name) + goto out; + + while (fgets(buf, sizeof(buf), f)) { + mapname[0] = '\0'; + res = sscanf(buf, "%llx-%llx %*s %*x %*s %*d %"STRINGIFY(PATH_MAX)"s", + &begin, &end, mapname); + if (res == 3 && mapname[0] != '\0') { + map = realloc(maps->lib_maps, + (maps->nr_lib_maps + 1) * sizeof(*map)); + if (!map) + goto out_fail; + map[maps->nr_lib_maps].end = end; + map[maps->nr_lib_maps].start = begin; + map[maps->nr_lib_maps].lib_name = strdup(mapname); + if (!map[maps->nr_lib_maps].lib_name) + goto out_fail; + maps->lib_maps = map; + maps->nr_lib_maps++; + } + } +out: + fclose(f); + return 0; + +out_fail: + fclose(f); + if (maps) { + for (i = 0; i < maps->nr_lib_maps; i++) + free(maps->lib_maps[i].lib_name); + if (instance->pid_maps != maps) { + m = instance->pid_maps; + while (m) { + if (m->next == maps) { + m->next = maps->next; + break; + } + m = m->next; + } + } else + instance->pid_maps = maps->next; + free(maps->lib_maps); + maps->lib_maps = NULL; + maps->nr_lib_maps = 0; + free(maps->proc_name); + maps->proc_name = NULL; + free(maps); + } + return ret; +} + +static void get_filter_pid_maps(void) +{ + struct filter_pids *p; + + for (p = filter_pids; p; p = p->next) { + if (p->exclude) + continue; + get_pid_addr_maps(p->pid); + } +} + static void update_task_filter(void) { struct buffer_instance *instance; @@ -1076,6 +1193,9 @@ static void update_task_filter(void) if (no_filter) return; + if (get_procmap && filter_pids) + get_filter_pid_maps(); + if (filter_task) add_filter_pid(pid, 0); @@ -1289,6 +1409,8 @@ static void ptrace_wait(enum trace_type type) break; case PTRACE_EVENT_EXIT: + if (get_procmap) + get_pid_addr_maps(pid); ptrace(PTRACE_GETEVENTMSG, pid, NULL, &cstatus); ptrace(PTRACE_DETACH, pid, NULL, NULL); break; @@ -1365,6 +1487,7 @@ static void run_cmd(enum trace_type type, int argc, char **argv) } if (do_ptrace) { add_filter_pid(pid, 0); + ptrace_attach(pid); ptrace_wait(type); } else trace_waitpid(type, pid, &status, 0); @@ -3132,6 +3255,36 @@ static void append_buffer(struct tracecmd_output *handle, } } + +static void +add_pid_maps(struct tracecmd_output *handle, struct buffer_instance *instance) +{ + struct pid_addr_maps *maps = instance->pid_maps; + struct trace_seq s; + int i; + + trace_seq_init(&s); + while (maps) { + if (!maps->nr_lib_maps) { + maps = maps->next; + continue; + } + trace_seq_reset(&s); + trace_seq_printf(&s, "%x %x %s\n", + maps->pid, maps->nr_lib_maps, maps->proc_name); + for (i = 0; i < maps->nr_lib_maps; i++) + trace_seq_printf(&s, "%llx %llx %s\n", + maps->lib_maps[i].start, + maps->lib_maps[i].end, + maps->lib_maps[i].lib_name); + trace_seq_terminate(&s); + tracecmd_add_option(handle, TRACECMD_OPTION_PROCMAPS, + s.len + 1, s.buffer); + maps = maps->next; + } + trace_seq_destroy(&s); +} + static void add_buffer_stat(struct tracecmd_output *handle, struct buffer_instance *instance) { @@ -3325,6 +3478,10 @@ static void record_data(struct common_record_context *ctx) if (!no_top_instance() && !top_instance.msg_handle) print_stat(&top_instance); + for_all_instances(instance) { + add_pid_maps(handle, instance); + } + tracecmd_append_cpu_data(handle, local_cpu_count, temp_files); for (i = 0; i < max_cpu_count; i++) @@ -4435,6 +4592,7 @@ void update_first_instance(struct buffer_instance *instance, int topt) } enum { + OPT_procmap = 244, OPT_quiet = 245, OPT_debug = 246, OPT_no_filter = 247, @@ -4665,6 +4823,7 @@ static void parse_record_options(int argc, {"debug", no_argument, NULL, OPT_debug}, {"quiet", no_argument, NULL, OPT_quiet}, {"help", no_argument, NULL, '?'}, + {"proc-map", no_argument, NULL, OPT_procmap}, {"module", required_argument, NULL, OPT_module}, {NULL, 0, NULL, 0} }; @@ -4754,6 +4913,7 @@ static void parse_record_options(int argc, die("-c invalid: ptrace not supported"); #endif do_ptrace = 1; + do_children = 1; } else { save_option("event-fork"); ctx->do_child = 1; @@ -4896,6 +5056,9 @@ static void parse_record_options(int argc, case 'i': ignore_event_not_found = 1; break; + case OPT_procmap: + get_procmap = 1; + break; case OPT_date: ctx->date = 1; if (ctx->data_flags & DATA_FL_OFFSET) @@ -4962,7 +5125,7 @@ static void parse_record_options(int argc, add_func(&ctx->instance->filter_funcs, ctx->instance->filter_mod, "*"); - if (do_ptrace && !filter_task && !nr_filter_pids) + if (do_children && !filter_task && !nr_filter_pids) die(" -c can only be used with -F (or -P with event-fork support)"); if (ctx->do_child && !filter_task && !nr_filter_pids) die(" -c can only be used with -P or -F"); @@ -4976,6 +5139,13 @@ static void parse_record_options(int argc, "Did you mean 'record'?"); ctx->run_command = 1; } + + if (get_procmap) { + if (!ctx->run_command && !nr_filter_pids) + warning("--proc-map is ignored, no command or filtered PIDs are specified."); + else + do_ptrace = 1; + } } static enum trace_type get_trace_cmd_type(enum trace_cmd cmd) diff --git a/tracecmd/trace-usage.c b/tracecmd/trace-usage.c index 406384c..7a67784 100644 --- a/tracecmd/trace-usage.c +++ b/tracecmd/trace-usage.c @@ -57,6 +57,7 @@ static struct usage_help usage_help[] = { " (use with caution)\n" " --max-graph-depth limit function_graph depth\n" " --no-filter include trace-cmd threads in the trace\n" + " --proc-map save the traced processes address map into the trace.dat file\n" }, { "start", From patchwork Wed Aug 28 11:24:57 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Tzvetomir Stoyanov (VMware)" X-Patchwork-Id: 11118725 X-Patchwork-Delegate: rostedt@goodmis.org Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 4D23A14F7 for ; Wed, 28 Aug 2019 11:25:07 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 2C24322CF5 for ; Wed, 28 Aug 2019 11:25:07 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="XKAr8GeT" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726408AbfH1LZH (ORCPT ); Wed, 28 Aug 2019 07:25:07 -0400 Received: from mail-wm1-f68.google.com ([209.85.128.68]:36828 "EHLO mail-wm1-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726368AbfH1LZG (ORCPT ); Wed, 28 Aug 2019 07:25:06 -0400 Received: by mail-wm1-f68.google.com with SMTP id p13so2233378wmh.1 for ; Wed, 28 Aug 2019 04:25:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=ueWojPE0uYaqhVaCkvP31HnbARge98KstgiOIw7e4Us=; b=XKAr8GeTXyZu7CO7hFcGuFvsue7Fiz8UqKOxm9d308W5HAJyV4bhHAm//Vt3RP8EIx O8b6CDj1z8tWSDC+FWFs76LTkRpXa6E4OCDHIHHwsPS3f6aghicj8n3+kA3xcL9tXSC/ 5ODp8aXOuZUQuGLXECI1WxGfaLoePTDAPAG3i9w728THUjl1TnlJri912QnveOu5kbQ2 ho+HXb7Ikz/33SzRNW3fxpU9ty0FQi3bRtnkRFzea+LnD05v6rlqP51/KA87c2J5Ru2N DLyqnLLBWk+h0s0yZRvoSn8neA2mJWL4g3/wd2qXk20kThZlnvmXM5q0px5nkmVElAOG cvrQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=ueWojPE0uYaqhVaCkvP31HnbARge98KstgiOIw7e4Us=; b=sTsD1iAXzxqikKennPczff3CFEgzPxLKVtLOnJOxKIoxUNsLpm3FeeqyMZXGqlNUNY Y9QnZKMG/fQlgE4zErZRb+TRK3KB1GIDIUzb7JbPS/RGTIKuv31znD/Fq6QOLkbs5H/f 3sJ894GiX/znXtd9igbtKd4trZauMwRbeCJ1kQgYoDjCaS5Edep43SWh0uM0RZzXUlHE cc0rBNf0+HOtmwiYrH3iCdgJsQy4g6rMWo+vJ3Xv9f4cVHvjcqxxSHyiTUaJ+clLjhR2 1uK9lNg+Vx2Vvwh0kQR+DnPyo6t220nm9hf73RHMtAmuFPi9k6krCBhrOYCEsIyvQarL 4l3w== X-Gm-Message-State: APjAAAWf3YqCIDD18mNSSU1W1wi55Xy3G2zlVMvt6JwciJdRthSmUwCT k1TFGTURLBi7zzc95honii+LzXLk X-Google-Smtp-Source: APXvYqwh7MYeRkfzDpYhRLTAr4npS6ierjo57tG2kCFu+n64C7QFQ1DCZ3MzNn8/txYfJ6KjcBkODg== X-Received: by 2002:a7b:c0d0:: with SMTP id s16mr4177028wmh.65.1566991503899; Wed, 28 Aug 2019 04:25:03 -0700 (PDT) Received: from oberon.eng.vmware.com ([146.247.46.5]) by smtp.gmail.com with ESMTPSA id c132sm2846766wme.27.2019.08.28.04.25.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 28 Aug 2019 04:25:03 -0700 (PDT) From: "Tzvetomir Stoyanov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org Subject: [PATCH v6 3/3] trace-cmd: Add option to execute traced process as given user Date: Wed, 28 Aug 2019 14:24:57 +0300 Message-Id: <20190828112457.10533-4-tz.stoyanov@gmail.com> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190828112457.10533-1-tz.stoyanov@gmail.com> References: <20190828112457.10533-1-tz.stoyanov@gmail.com> MIME-Version: 1.0 Sender: linux-trace-devel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org A new trace-cmd record option is added: "--user". When it is set with combination of option -F, the traced process is executed in the context of the specified user. Signed-off-by: Tzvetomir Stoyanov (VMware) --- Documentation/trace-cmd-record.1.txt | 3 ++ tracecmd/trace-record.c | 49 ++++++++++++++++++++++++++-- tracecmd/trace-usage.c | 1 + 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/Documentation/trace-cmd-record.1.txt b/Documentation/trace-cmd-record.1.txt index e697f03..bb18e88 100644 --- a/Documentation/trace-cmd-record.1.txt +++ b/Documentation/trace-cmd-record.1.txt @@ -119,6 +119,9 @@ OPTIONS Used with either *-F* (or *-P* if kernel supports it) to trace the process' children too. +*--user*:: + Execute the specified *command* as given user. + *-C* 'clock':: Set the trace clock to "clock". diff --git a/tracecmd/trace-record.c b/tracecmd/trace-record.c index 4a20339..9b7357f 100644 --- a/tracecmd/trace-record.c +++ b/tracecmd/trace-record.c @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include "version.h" #include "trace-local.h" @@ -208,6 +210,7 @@ struct common_record_context { struct buffer_instance *instance; const char *output; char *date2ts; + char *user; int data_flags; int record_all; @@ -1457,7 +1460,34 @@ static void trace_or_sleep(enum trace_type type) sleep(10); } -static void run_cmd(enum trace_type type, int argc, char **argv) +static int change_user(const char *user) +{ + struct passwd *pwd; + + if (!user) + return 0; + + pwd = getpwnam(user); + if (!pwd) + return -1; + if (initgroups(user, pwd->pw_gid) < 0) + return -1; + if (setgid(pwd->pw_gid) < 0) + return -1; + if (setuid(pwd->pw_uid) < 0) + return -1; + + if (setenv("HOME", pwd->pw_dir, 1) < 0) + return -1; + if (setenv("USER", pwd->pw_name, 1) < 0) + return -1; + if (setenv("LOGNAME", pwd->pw_name, 1) < 0) + return -1; + + return 0; +} + +static void run_cmd(enum trace_type type, const char *user, int argc, char **argv) { int status; int pid; @@ -1478,6 +1508,10 @@ static void run_cmd(enum trace_type type, int argc, char **argv) dup2(save_stdout, 1); close(save_stdout); } + + if (change_user(user) < 0) + die("Failed to change user to %s", user); + if (execvp(argv[0], argv)) { fprintf(stderr, "\n********************\n"); fprintf(stderr, " Unable to exec %s\n", argv[0]); @@ -4592,6 +4626,7 @@ void update_first_instance(struct buffer_instance *instance, int topt) } enum { + OPT_user = 243, OPT_procmap = 244, OPT_quiet = 245, OPT_debug = 246, @@ -4824,6 +4859,7 @@ static void parse_record_options(int argc, {"quiet", no_argument, NULL, OPT_quiet}, {"help", no_argument, NULL, '?'}, {"proc-map", no_argument, NULL, OPT_procmap}, + {"user", required_argument, NULL, OPT_user}, {"module", required_argument, NULL, OPT_module}, {NULL, 0, NULL, 0} }; @@ -5056,6 +5092,11 @@ static void parse_record_options(int argc, case 'i': ignore_event_not_found = 1; break; + case OPT_user: + ctx->user = strdup(optarg); + if (!ctx->user) + die("Failed to allocate user name"); + break; case OPT_procmap: get_procmap = 1; break; @@ -5139,7 +5180,9 @@ static void parse_record_options(int argc, "Did you mean 'record'?"); ctx->run_command = 1; } - + if (ctx->user && !ctx->run_command) + warning("--user %s is ignored, no command is specified", + ctx->user); if (get_procmap) { if (!ctx->run_command && !nr_filter_pids) warning("--proc-map is ignored, no command or filtered PIDs are specified."); @@ -5287,7 +5330,7 @@ static void record_trace(int argc, char **argv, } if (ctx->run_command) - run_cmd(type, (argc - optind) - 1, &argv[optind + 1]); + run_cmd(type, ctx->user, (argc - optind) - 1, &argv[optind + 1]); else { update_task_filter(); tracecmd_enable_tracing(); diff --git a/tracecmd/trace-usage.c b/tracecmd/trace-usage.c index 7a67784..20cb66f 100644 --- a/tracecmd/trace-usage.c +++ b/tracecmd/trace-usage.c @@ -58,6 +58,7 @@ static struct usage_help usage_help[] = { " --max-graph-depth limit function_graph depth\n" " --no-filter include trace-cmd threads in the trace\n" " --proc-map save the traced processes address map into the trace.dat file\n" + " --user execute the specified [command ...] as given user\n" }, { "start",