Received: by 2002:a05:6358:3188:b0:123:57c1:9b43 with SMTP id q8csp19708506rwd; Wed, 28 Jun 2023 13:06:05 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ5++qMI+4KlyWbMPF4qjpxKxtkHvTpsOzGc997gZJ/ZzQKmSP0qX2bxhXSTgI5XkPb5u79W X-Received: by 2002:a17:907:7783:b0:992:7e1f:8419 with SMTP id ky3-20020a170907778300b009927e1f8419mr2218002ejc.2.1687982764754; Wed, 28 Jun 2023 13:06:04 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1687982764; cv=none; d=google.com; s=arc-20160816; b=srTnK07tI57npW7B1sQgTtyRqCfLuuaazN9wrHg3ykR/SBCewGonvtLophzLqp9u5Q iMnpnGHM4SLb2Wg31seIZaOQbisZHyK4AV+T8GpWs751MpmKtEIoPCJfFEEzs/px9HIW 1EG6OMoVsXhxYV5CF3qrXq4CZmxlmLkqyFsaDwbnCEl+COmVP5pj2npR9ESg70fm50VJ yGXNLG0+9o0hIrwYkv1SMD7g1rA1y/kHjkLzZM2qYPcwdN8QR2BHaslqqqj8G/Hbby6k UaFYRWE7hQwvtRhIZx3GDf1kobmF+mEmDth5daZ82Ptc7HbgKnwYwwvlTKb7uSJrSwzX 40XA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from:sender :dkim-signature; bh=PiWZYv4c06sxae6xC2ziSZRNA4QZoNeA1AhON9n731k=; fh=qg0XpHrrfjSy6nePy3ZLIayRkwEhRnO8Y6NeBhcC42U=; b=OrcB6HygWR+U/9xAuFFezqu5kSuGYcRLPlL4YOcVOATegAeesVEvQcU+mrDPghVT0W ooPii95uvKWvdu5rTVUrNr9i5IycCuWBNWcy198beF927bVidqvU4WnDBpkYIixIEWrI vXoH43BvDh6nXg+Nei5I9kg8SY4BQxd501kLDQcQk0ZLQjLgP4HsTNzVv7J5qAkf+xnZ EktIFmfZ3OSZ57hpJ7Jg8XCwgRmWkAo2ge/3Ep6PBa+EG4W7utMmLPHOUAFtz4Pwtvy3 06QYjveqGvxhVRxhlZUxrazMJGeCyVebpxWwoa8wVoekmDv0zd+ei3OLuHlgdPmz8HVn xhtA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20221208 header.b="SIX1h/Jb"; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id u26-20020a1709064ada00b00991b9b54b2asi4299254ejt.135.2023.06.28.13.05.36; Wed, 28 Jun 2023 13:06:04 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20221208 header.b="SIX1h/Jb"; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231833AbjF1UCF (ORCPT + 99 others); Wed, 28 Jun 2023 16:02:05 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59418 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231555AbjF1UBu (ORCPT ); Wed, 28 Jun 2023 16:01:50 -0400 Received: from mail-pf1-x42a.google.com (mail-pf1-x42a.google.com [IPv6:2607:f8b0:4864:20::42a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A8C782110; Wed, 28 Jun 2023 13:01:48 -0700 (PDT) Received: by mail-pf1-x42a.google.com with SMTP id d2e1a72fcca58-666eef03ebdso120917b3a.1; Wed, 28 Jun 2023 13:01:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1687982508; x=1690574508; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:from:to:cc:subject:date :message-id:reply-to; bh=PiWZYv4c06sxae6xC2ziSZRNA4QZoNeA1AhON9n731k=; b=SIX1h/Jb8fu5N1K7e7gJJ2kt0hKA0VSaJBNwxaPyNAghmGdX64yRxNgXqc145Gq6Zn Q7tUj7op8yPoFEb+GiHPkjHwso/tegnJuMh4Hndgwbhf1HkgYPQa+enWIvW7bM6QI+0z i7aHZhWjcFgphEb+wDE/izT1MGfuERMG/Sgj1KrykFeCFihlAH0ePbIAIFjkEThufgHy QpWq85mkEeX26kPCEDiQ724MDwkytXK0NW2DFa74Nl2ehLv4uW36WcwFqC7c7C8e8Ayd VZyUN2/25oS/QxrqWMtbFFzKSKw+soievxD3vRXjusM/yZ8XgtDN5mj7E34U1pGrdTFI YCKQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1687982508; x=1690574508; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=PiWZYv4c06sxae6xC2ziSZRNA4QZoNeA1AhON9n731k=; b=fc3EdQUBpujJEsE4wACVFHoQf525mnZJnTuQrZ0DI5a8ONiD+c4P7Y1qJU8IqauvQO Ul5fQ4fIhdDBChdb3NgXjo2e2CDoOvGj9DqMdavA1drMkcy/Uklkk7neP5ZZQTcwvOAA lxCCl3uOW3t2xEJJCiew4nIch5je9UgrtyV6ryrSgkoQycdkk3mOzB6sxcKvLZx7hdMW 9qeudcnRhvwNaoaQ6KVQ2pYlFmM0DRJHQ2KBM4xJC45U7OCxObmh6+RFY3emxmMrciH7 xU2h/EJHNrV2kO2VBZ40iFgPRukVbzbUw63IiKBKmzr3vVdeHTc18+z8ZMHLnkSb2vKO Z4ig== X-Gm-Message-State: AC+VfDx1pvG4cG52kyidmrH3/m5dfyONtXnmP6Ws7zvNNxcLrTeKCRea HdYRovJqcsekhv3vMU7bkS4= X-Received: by 2002:a05:6a20:3c89:b0:124:eea9:668d with SMTP id b9-20020a056a203c8900b00124eea9668dmr17069108pzj.40.1687982507844; Wed, 28 Jun 2023 13:01:47 -0700 (PDT) Received: from bangji.corp.google.com ([2620:15c:2c0:5:fc6b:e50c:c8f0:b09b]) by smtp.gmail.com with ESMTPSA id f3-20020aa78b03000000b0065e154bac6dsm7340376pfd.133.2023.06.28.13.01.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 28 Jun 2023 13:01:47 -0700 (PDT) Sender: Namhyung Kim From: Namhyung Kim To: Arnaldo Carvalho de Melo , Jiri Olsa Cc: Ian Rogers , Adrian Hunter , Peter Zijlstra , Ingo Molnar , LKML , linux-perf-users@vger.kernel.org, Song Liu , Hao Luo Subject: [PATCH 3/4] perf lock contention: Add --output option Date: Wed, 28 Jun 2023 13:01:40 -0700 Message-ID: <20230628200141.2739587-4-namhyung@kernel.org> X-Mailer: git-send-email 2.41.0.255.g8b1d071c50-goog In-Reply-To: <20230628200141.2739587-1-namhyung@kernel.org> References: <20230628200141.2739587-1-namhyung@kernel.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-1.5 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_EF,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE, SPF_PASS,T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=no autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org To avoid formatting failures for example in CSV output due to debug messages, add --output option to put the result in a file. Unfortunately the short -o option was taken by the --owner already. $ sudo ./perf lock con -ab --output lock-out.txt -v sleep 1 Looking at the vmlinux_path (8 entries long) symsrc__init: cannot get elf header. Using /proc/kcore for kernel data Using /proc/kallsyms for symbols $ head lock-out.txt contended total wait max wait avg wait type caller 3 76.79 us 26.89 us 25.60 us rwlock:R ep_poll_callback+0x2d 0xffffffff9a23f4b5 _raw_read_lock_irqsave+0x45 0xffffffff99bbd4dd ep_poll_callback+0x2d 0xffffffff999029f3 __wake_up_common+0x73 0xffffffff99902b82 __wake_up_common_lock+0x82 0xffffffff99fa5b1c sock_def_readable+0x3c 0xffffffff9a11521d unix_stream_sendmsg+0x18d 0xffffffff99f9fc9c sock_sendmsg+0x5c Suggested-by: Ian Rogers Signed-off-by: Namhyung Kim --- tools/perf/Documentation/perf-lock.txt | 3 + tools/perf/builtin-lock.c | 140 +++++++++++++++---------- 2 files changed, 85 insertions(+), 58 deletions(-) diff --git a/tools/perf/Documentation/perf-lock.txt b/tools/perf/Documentation/perf-lock.txt index d1efcbe7a470..30eea576721f 100644 --- a/tools/perf/Documentation/perf-lock.txt +++ b/tools/perf/Documentation/perf-lock.txt @@ -36,6 +36,9 @@ COMMON OPTIONS --input=:: Input file name. (default: perf.data unless stdin is a fifo) +--output=:: + Output file name for perf lock contention and report. + -v:: --verbose:: Be more verbose (show symbol address, etc). diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 98b0c0b1b307..c15386cb1033 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -27,6 +27,7 @@ #include "util/map.h" #include "util/util.h" +#include #include #include #include @@ -65,6 +66,8 @@ static int max_stack_depth = CONTENTION_STACK_DEPTH; static int stack_skip = CONTENTION_STACK_SKIP; static int print_nr_entries = INT_MAX / 2; static LIST_HEAD(callstack_filters); +static const char *output_name = NULL; +static FILE *lock_output; struct callstack_filter { struct list_head list; @@ -227,7 +230,7 @@ static void lock_stat_key_print_time(unsigned long long nsec, int len) /* for CSV output */ if (len == 0) { - pr_info("%llu", nsec); + fprintf(lock_output, "%llu", nsec); return; } @@ -235,18 +238,18 @@ static void lock_stat_key_print_time(unsigned long long nsec, int len) if (nsec < table[i].base) continue; - pr_info("%*.2f %s", len - 3, nsec / table[i].base, table[i].unit); + fprintf(lock_output, "%*.2f %s", len - 3, nsec / table[i].base, table[i].unit); return; } - pr_info("%*llu %s", len - 3, nsec, "ns"); + fprintf(lock_output, "%*llu %s", len - 3, nsec, "ns"); } #define PRINT_KEY(member) \ static void lock_stat_key_print_ ## member(struct lock_key *key, \ struct lock_stat *ls) \ { \ - pr_info("%*llu", key->len, (unsigned long long)ls->member); \ + fprintf(lock_output, "%*llu", key->len, (unsigned long long)ls->member);\ } #define PRINT_TIME(member) \ @@ -1335,12 +1338,12 @@ static void print_bad_events(int bad, int total) if (quiet || total == 0 || (broken == 0 && verbose <= 0)) return; - pr_info("\n=== output for debug ===\n\n"); - pr_info("bad: %d, total: %d\n", bad, total); - pr_info("bad rate: %.2f %%\n", (double)bad / (double)total * 100); - pr_info("histogram of events caused bad sequence\n"); + fprintf(lock_output, "\n=== output for debug ===\n\n"); + fprintf(lock_output, "bad: %d, total: %d\n", bad, total); + fprintf(lock_output, "bad rate: %.2f %%\n", (double)bad / (double)total * 100); + fprintf(lock_output, "histogram of events caused bad sequence\n"); for (i = 0; i < BROKEN_MAX; i++) - pr_info(" %10s: %d\n", name[i], bad_hist[i]); + fprintf(lock_output, " %10s: %d\n", name[i], bad_hist[i]); } /* TODO: various way to print, coloring, nano or milli sec */ @@ -1352,10 +1355,10 @@ static void print_result(void) int bad, total, printed; if (!quiet) { - pr_info("%20s ", "Name"); + fprintf(lock_output, "%20s ", "Name"); list_for_each_entry(key, &lock_keys, list) - pr_info("%*s ", key->len, key->header); - pr_info("\n\n"); + fprintf(lock_output, "%*s ", key->len, key->header); + fprintf(lock_output, "\n\n"); } bad = total = printed = 0; @@ -1380,7 +1383,7 @@ static void print_result(void) name = thread__comm_str(t); } - pr_info("%20s ", name); + fprintf(lock_output, "%20s ", name); } else { strncpy(cut_name, st->name, 16); cut_name[16] = '.'; @@ -1388,14 +1391,14 @@ static void print_result(void) cut_name[18] = '.'; cut_name[19] = '\0'; /* cut off name for saving output style */ - pr_info("%20s ", cut_name); + fprintf(lock_output, "%20s ", cut_name); } list_for_each_entry(key, &lock_keys, list) { key->print(key, st); - pr_info(" "); + fprintf(lock_output, " "); } - pr_info("\n"); + fprintf(lock_output, "\n"); if (++printed >= print_nr_entries) break; @@ -1412,13 +1415,13 @@ static void dump_threads(void) struct rb_node *node; struct thread *t; - pr_info("%10s: comm\n", "Thread ID"); + fprintf(lock_output, "%10s: comm\n", "Thread ID"); node = rb_first(&thread_stats); while (node) { st = container_of(node, struct thread_stat, rb); t = perf_session__findnew(session, st->tid); - pr_info("%10d: %s\n", st->tid, thread__comm_str(t)); + fprintf(lock_output, "%10d: %s\n", st->tid, thread__comm_str(t)); node = rb_next(node); thread__put(t); } @@ -1444,7 +1447,7 @@ static void dump_map(void) unsigned int i; struct lock_stat *st; - pr_info("Address of instance: name of class\n"); + fprintf(lock_output, "Address of instance: name of class\n"); for (i = 0; i < LOCKHASH_SIZE; i++) { hlist_for_each_entry(st, &lockhash_table[i], hash_entry) { insert_to_result(st, compare_maps); @@ -1452,7 +1455,7 @@ static void dump_map(void) } while ((st = pop_from_result())) - pr_info(" %#llx: %s\n", (unsigned long long)st->addr, st->name); + fprintf(lock_output, " %#llx: %s\n", (unsigned long long)st->addr, st->name); } static int dump_info(void) @@ -1637,18 +1640,18 @@ static void print_header_stdio(void) struct lock_key *key; list_for_each_entry(key, &lock_keys, list) - pr_info("%*s ", key->len, key->header); + fprintf(lock_output, "%*s ", key->len, key->header); switch (aggr_mode) { case LOCK_AGGR_TASK: - pr_info(" %10s %s\n\n", "pid", + fprintf(lock_output, " %10s %s\n\n", "pid", show_lock_owner ? "owner" : "comm"); break; case LOCK_AGGR_CALLER: - pr_info(" %10s %s\n\n", "type", "caller"); + fprintf(lock_output, " %10s %s\n\n", "type", "caller"); break; case LOCK_AGGR_ADDR: - pr_info(" %16s %s\n\n", "address", "symbol"); + fprintf(lock_output, " %16s %s\n\n", "address", "symbol"); break; default: break; @@ -1659,23 +1662,23 @@ static void print_header_csv(const char *sep) { struct lock_key *key; - pr_info("# output: "); + fprintf(lock_output, "# output: "); list_for_each_entry(key, &lock_keys, list) - pr_info("%s%s ", key->header, sep); + fprintf(lock_output, "%s%s ", key->header, sep); switch (aggr_mode) { case LOCK_AGGR_TASK: - pr_info("%s%s %s\n", "pid", sep, + fprintf(lock_output, "%s%s %s\n", "pid", sep, show_lock_owner ? "owner" : "comm"); break; case LOCK_AGGR_CALLER: - pr_info("%s%s %s", "type", sep, "caller"); + fprintf(lock_output, "%s%s %s", "type", sep, "caller"); if (verbose > 0) - pr_info("%s %s", sep, "stacktrace"); - pr_info("\n"); + fprintf(lock_output, "%s %s", sep, "stacktrace"); + fprintf(lock_output, "\n"); break; case LOCK_AGGR_ADDR: - pr_info("%s%s %s%s %s\n", "address", sep, "symbol", sep, "type"); + fprintf(lock_output, "%s%s %s%s %s\n", "address", sep, "symbol", sep, "type"); break; default: break; @@ -1700,21 +1703,21 @@ static void print_lock_stat_stdio(struct lock_contention *con, struct lock_stat list_for_each_entry(key, &lock_keys, list) { key->print(key, st); - pr_info(" "); + fprintf(lock_output, " "); } switch (aggr_mode) { case LOCK_AGGR_CALLER: - pr_info(" %10s %s\n", get_type_str(st->flags), st->name); + fprintf(lock_output, " %10s %s\n", get_type_str(st->flags), st->name); break; case LOCK_AGGR_TASK: pid = st->addr; t = perf_session__findnew(session, pid); - pr_info(" %10d %s\n", + fprintf(lock_output, " %10d %s\n", pid, pid == -1 ? "Unknown" : thread__comm_str(t)); break; case LOCK_AGGR_ADDR: - pr_info(" %016llx %s (%s)\n", (unsigned long long)st->addr, + fprintf(lock_output, " %016llx %s (%s)\n", (unsigned long long)st->addr, st->name, get_type_name(st->flags)); break; default: @@ -1734,7 +1737,7 @@ static void print_lock_stat_stdio(struct lock_contention *con, struct lock_stat ip = st->callstack[i]; sym = machine__find_kernel_symbol(con->machine, ip, &kmap); get_symbol_name_offset(kmap, sym, ip, buf, sizeof(buf)); - pr_info("\t\t\t%#lx %s\n", (unsigned long)ip, buf); + fprintf(lock_output, "\t\t\t%#lx %s\n", (unsigned long)ip, buf); } } } @@ -1748,22 +1751,23 @@ static void print_lock_stat_csv(struct lock_contention *con, struct lock_stat *s list_for_each_entry(key, &lock_keys, list) { key->print(key, st); - pr_info("%s ", sep); + fprintf(lock_output, "%s ", sep); } switch (aggr_mode) { case LOCK_AGGR_CALLER: - pr_info("%s%s %s", get_type_str(st->flags), sep, st->name); + fprintf(lock_output, "%s%s %s", get_type_str(st->flags), sep, st->name); if (verbose <= 0) - pr_info("\n"); + fprintf(lock_output, "\n"); break; case LOCK_AGGR_TASK: pid = st->addr; t = perf_session__findnew(session, pid); - pr_info("%d%s %s\n", pid, sep, pid == -1 ? "Unknown" : thread__comm_str(t)); + fprintf(lock_output, "%d%s %s\n", pid, sep, + pid == -1 ? "Unknown" : thread__comm_str(t)); break; case LOCK_AGGR_ADDR: - pr_info("%llx%s %s%s %s\n", (unsigned long long)st->addr, sep, + fprintf(lock_output, "%llx%s %s%s %s\n", (unsigned long long)st->addr, sep, st->name, sep, get_type_name(st->flags)); break; default: @@ -1783,9 +1787,9 @@ static void print_lock_stat_csv(struct lock_contention *con, struct lock_stat *s ip = st->callstack[i]; sym = machine__find_kernel_symbol(con->machine, ip, &kmap); get_symbol_name_offset(kmap, sym, ip, buf, sizeof(buf)); - pr_info("%s %#lx %s", i ? ":" : sep, (unsigned long) ip, buf); + fprintf(lock_output, "%s %#lx %s", i ? ":" : sep, (unsigned long) ip, buf); } - pr_info("\n"); + fprintf(lock_output, "\n"); } } @@ -1809,15 +1813,15 @@ static void print_footer_stdio(int total, int bad, struct lock_contention_fails return; total += broken; - pr_info("\n=== output for debug ===\n\n"); - pr_info("bad: %d, total: %d\n", broken, total); - pr_info("bad rate: %.2f %%\n", (double)broken / (double)total * 100); + fprintf(lock_output, "\n=== output for debug ===\n\n"); + fprintf(lock_output, "bad: %d, total: %d\n", broken, total); + fprintf(lock_output, "bad rate: %.2f %%\n", 100.0 * broken / total); - pr_info("histogram of failure reasons\n"); - pr_info(" %10s: %d\n", "task", fails->task); - pr_info(" %10s: %d\n", "stack", fails->stack); - pr_info(" %10s: %d\n", "time", fails->time); - pr_info(" %10s: %d\n", "data", fails->data); + fprintf(lock_output, "histogram of failure reasons\n"); + fprintf(lock_output, " %10s: %d\n", "task", fails->task); + fprintf(lock_output, " %10s: %d\n", "stack", fails->stack); + fprintf(lock_output, " %10s: %d\n", "time", fails->time); + fprintf(lock_output, " %10s: %d\n", "data", fails->data); } static void print_footer_csv(int total, int bad, struct lock_contention_fails *fails, @@ -1831,21 +1835,21 @@ static void print_footer_csv(int total, int bad, struct lock_contention_fails *f return; total += bad; - pr_info("# debug: total=%d%s bad=%d", total, sep, bad); + fprintf(lock_output, "# debug: total=%d%s bad=%d", total, sep, bad); if (use_bpf) { - pr_info("%s bad_%s=%d", sep, "task", fails->task); - pr_info("%s bad_%s=%d", sep, "stack", fails->stack); - pr_info("%s bad_%s=%d", sep, "time", fails->time); - pr_info("%s bad_%s=%d", sep, "data", fails->data); + fprintf(lock_output, "%s bad_%s=%d", sep, "task", fails->task); + fprintf(lock_output, "%s bad_%s=%d", sep, "stack", fails->stack); + fprintf(lock_output, "%s bad_%s=%d", sep, "time", fails->time); + fprintf(lock_output, "%s bad_%s=%d", sep, "data", fails->data); } else { int i; const char *name[4] = { "acquire", "acquired", "contended", "release" }; for (i = 0; i < BROKEN_MAX; i++) - pr_info("%s bad_%s=%d", sep, name[i], bad_hist[i]); + fprintf(lock_output, "%s bad_%s=%d", sep, name[i], bad_hist[i]); } - pr_info("\n"); + fprintf(lock_output, "\n"); } static void print_footer(int total, int bad, struct lock_contention_fails *fails) @@ -2427,10 +2431,29 @@ static int parse_call_stack(const struct option *opt __maybe_unused, const char return ret; } +static int parse_output(const struct option *opt __maybe_unused, const char *str, + int unset __maybe_unused) +{ + const char **name = (const char **)opt->value; + + if (str == NULL) + return -1; + + lock_output = fopen(str, "w"); + if (lock_output == NULL) { + pr_err("Cannot open %s\n", str); + return -1; + } + + *name = str; + return 0; +} + int cmd_lock(int argc, const char **argv) { const struct option lock_options[] = { OPT_STRING('i', "input", &input_name, "file", "input file name"), + OPT_CALLBACK(0, "output", &output_name, "file", "output file name", parse_output), OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), @@ -2530,6 +2553,7 @@ int cmd_lock(int argc, const char **argv) for (i = 0; i < LOCKHASH_SIZE; i++) INIT_HLIST_HEAD(lockhash_table + i); + lock_output = stderr; argc = parse_options_subcommand(argc, argv, lock_options, lock_subcommands, lock_usage, PARSE_OPT_STOP_AT_NON_OPTION); if (!argc) -- 2.41.0.255.g8b1d071c50-goog