Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F173584617
D59855.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
9 KB
Referenced Files
None
Subscribers
None
D59855.id.diff
View Options
diff --git a/usr.sbin/pmc/Makefile b/usr.sbin/pmc/Makefile
--- a/usr.sbin/pmc/Makefile
+++ b/usr.sbin/pmc/Makefile
@@ -12,7 +12,7 @@
SRCS= pmc.c pmc_util.c cmd_pmc_stat.c \
cmd_pmc_list.c cmd_pmc_filter.cc \
cmd_pmc_summary.cc \
- cmd_pmc_annotate.cc cmd_pmc_branch.cc \
+ cmd_pmc_annotate.cc cmd_pmc_branch.cc cmd_pmc_calltree.cc \
cmd_pmc_frontend.cc cmd_pmc_ibs.cc cmd_pmc_info.cc \
cmd_pmc_memory.cc cmd_pmc_program.cc cmd_pmc_record.cc \
cmd_pmc_system.cc \
diff --git a/usr.sbin/pmc/cmd_pmc.h b/usr.sbin/pmc/calltree.hh
copy from usr.sbin/pmc/cmd_pmc.h
copy to usr.sbin/pmc/calltree.hh
--- a/usr.sbin/pmc/cmd_pmc.h
+++ b/usr.sbin/pmc/calltree.hh
@@ -1,7 +1,10 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018, Matthew Macy
+ * Copyright (c) 2026, Netflix, Inc.
+ *
+ * This software was developed by Ali Mashtizadeh under the sponsorship from
+ * Netflix, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -25,43 +28,48 @@
* SUCH DAMAGE.
*
*/
-#ifndef _CMD_PMC_H_
-#define _CMD_PMC_H_
-#define DEFAULT_DISPLAY_HEIGHT 256 /* file virtual height */
-#define DEFAULT_DISPLAY_WIDTH 1024 /* file virtual width */
-extern int pmc_displayheight;
-extern int pmc_displaywidth;
-extern int pmc_kq;
-extern struct pmcstat_args pmc_args;
+class calltree {
+public:
+ struct callnode {
+ callnode() { }
+ callnode(const std::string &arg) : samples(0), name(arg), children() { }
+ ~callnode() { }
+ uint64_t samples;
+ std::string name;
+ std::unordered_map<std::string, callnode> children;
+ };
+
+ callnode root;
+
+ calltree() : root("All Samples") { }
+ ~calltree() { }
+ /*
+ * Constructs a calltree from samples that are root to leaf ordered.\
+ */
+ void addsample(std::vector<std::string> stack)
+ {
+ if (stack.size() == 0)
+ return;
+
+ callnode* current = &root;
+ root.samples++;
+
+ for (std::string& frame : stack) {
+ callnode* next = nullptr;
+
+ auto child = current->children.find(frame);
+ if (child == current->children.end()) {
+ current->children[frame] = callnode(frame);
+ child = current->children.find(frame);
+ }
-typedef int (*cmd_disp_t)(int, char **);
+ next = &(child->second);
+ next->samples++;
-#if defined(__cplusplus)
-extern "C" {
-#endif
- int cmd_pmc_annotate(int, char **);
- int cmd_pmc_branch(int, char **);
- int cmd_pmc_ibs(int, char **);
- int cmd_pmc_info(int, char **);
- int cmd_pmc_filter(int, char **);
- int cmd_pmc_frontend(int, char **);
- int cmd_pmc_list_events(int, char **);
- int cmd_pmc_memory(int, char **);
- int cmd_pmc_program(int, char **);
- int cmd_pmc_record(int, char **);
- int cmd_pmc_stat(int, char **);
- int cmd_pmc_stat_system(int, char **);
- int cmd_pmc_summary(int, char **);
- int cmd_pmc_system(int, char **);
-#if defined(__cplusplus)
+ current = next;
+ }
+ }
};
-#endif
-int pmc_util_get_pid(struct pmcstat_args *);
-void pmc_util_start_pmcs(struct pmcstat_args *);
-void pmc_util_cleanup(struct pmcstat_args *);
-void pmc_util_shutdown_logging(struct pmcstat_args *args);
-void pmc_util_kill_process(struct pmcstat_args *args);
-#endif
diff --git a/usr.sbin/pmc/cmd_pmc.h b/usr.sbin/pmc/cmd_pmc.h
--- a/usr.sbin/pmc/cmd_pmc.h
+++ b/usr.sbin/pmc/cmd_pmc.h
@@ -43,6 +43,7 @@
#endif
int cmd_pmc_annotate(int, char **);
int cmd_pmc_branch(int, char **);
+ int cmd_pmc_calltree(int, char **);
int cmd_pmc_ibs(int, char **);
int cmd_pmc_info(int, char **);
int cmd_pmc_filter(int, char **);
diff --git a/usr.sbin/pmc/cmd_pmc_calltree.cc b/usr.sbin/pmc/cmd_pmc_calltree.cc
new file mode 100644
--- /dev/null
+++ b/usr.sbin/pmc/cmd_pmc_calltree.cc
@@ -0,0 +1,214 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026, Netflix, Inc.
+ *
+ * This software was developed by Ali Mashtizadeh under the sponsorship from
+ * Netflix, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/cpuset.h>
+#include <sys/event.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/ttycom.h>
+#include <sys/user.h>
+#include <sys/wait.h>
+
+#include <assert.h>
+#include <curses.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <kvm.h>
+#include <libgen.h>
+#include <limits.h>
+#include <locale.h>
+#include <math.h>
+#include <pmc.h>
+#include <pmclog.h>
+#include <regex.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "cmd_pmc.h"
+
+#include <algorithm>
+#include <iostream>
+#include <map>
+#include <set>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "display.hh"
+#include "view.hh"
+#include "calltree.hh"
+
+#define INDENT_WIDTH 5
+
+/* By default we prune anything below 0.1% */
+static float pct = 0.1;
+
+class calltree_view : public pmcview
+{
+public:
+ calltree_view() : tree() { }
+ ~calltree_view() { }
+
+ /*
+ * Resolve the symbols and turn them into a canonical string then
+ * reverse the vector and to the tree.
+ */
+ virtual void
+ callchain(struct pmclog_ev_callchain &p, uintfptr_t *cc, int len)
+ {
+ std::vector<std::string> stk;
+
+ for (int i = 0; i < len; i++) {
+ int usermode = (cc[i] >> (sizeof(uintfptr_t) * 8 - 1)) == 0;
+
+ if (cc[i] == 0)
+ break;
+
+ syminfo sym = addrtosymbol(usermode ? p.pl_pid : 0, cc[i]);
+ if (sym.binary != "") {
+ stk.push_back("[" + sym.binary + "]" + sym.to_string(false));
+ } else {
+ continue;
+ }
+ }
+
+ std::reverse(stk.begin(), stk.end());
+
+ tree.addsample(stk);
+ }
+
+ virtual void
+ print(calltree::callnode *node, int indent)
+ {
+ std::vector<std::pair<std::string, uint64_t>> children;
+
+ if ((100.0 * node->samples / tree.root.samples) < pct)
+ return;
+
+ for (auto &n : node->children) {
+ children.emplace_back(n.second.name, n.second.samples);
+ }
+
+ std::sort(children.begin(), children.end(),
+ [](const auto& a, const auto& b) { return a.second > b.second; });
+
+ printf("%*s%3.3f%% %s\n", INDENT_WIDTH * indent, "",
+ 100.0 * node->samples / tree.root.samples,
+ node->name.c_str());
+ for (auto &n : children) {
+ print(&node->children[n.first], indent + 1);
+ }
+ }
+
+ virtual void
+ print()
+ {
+ print(&tree.root, 0);
+ }
+protected:
+ calltree tree;
+};
+
+
+static struct option longopts[] = {
+ PMCFILTER_LOPTS,
+ { "pct", required_argument, NULL, 1 },
+ { NULL, 0, NULL, 0 }
+};
+
+static void
+usage(void)
+{
+ printf("Usage: pmc calltree [options] [pmclog]\n\n");
+ printf("Display a calltree\n\n");
+ printf("Options:\n");
+ printf("\t--pct Percent threshold (default: %3.3f)\n", pct);
+ PMCFILTER_PRINTOPTS();
+}
+
+int
+cmd_pmc_calltree(int argc, char **argv)
+{
+ struct pmcfilter filter = pmcfilter();
+ const char *logfile = "default.log";
+ int option, logfd;
+
+ while ((option = getopt_long(argc, argv, PMCFILTER_SOPTS, longopts, NULL)) != -1) {
+ switch (option) {
+ PMCFILTER_CASE(filter);
+ case 1:
+ pct = atof(optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ exit(EX_USAGE);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc > 1) {
+ usage();
+ exit(EX_USAGE);
+ }
+ if (argc == 1)
+ logfile = argv[0];
+
+ setup_screen();
+
+ if ((logfd = open(logfile, O_RDONLY)) < 0) {
+ errx(EX_OSERR, "ERROR: Cannot open \"%s\" for reading: %s.", logfile,
+ strerror(errno));
+ }
+
+ calltree_view v = calltree_view();
+ v.setfilter(filter);
+ v.process(logfd);
+ v.print();
+
+ close(logfd);
+
+ return (EX_OK);
+}
diff --git a/usr.sbin/pmc/pmc.c b/usr.sbin/pmc/pmc.c
--- a/usr.sbin/pmc/pmc.c
+++ b/usr.sbin/pmc/pmc.c
@@ -63,6 +63,7 @@
static struct cmd_handler disp_table[] = {
{ "annotate", cmd_pmc_annotate, "Annotate assembly listings" },
{ "branch", cmd_pmc_branch, "Analyze branch misprediction" },
+ { "calltree", cmd_pmc_calltree, "Print calltree" },
{ "filter", cmd_pmc_filter, NULL },
{ "frontend", cmd_pmc_frontend, "Analyze front-end stalls" },
{ "ibs", cmd_pmc_ibs, "Breakdown IBS statistics" },
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Sep 28, 12:13 AM (17 h, 40 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
39736532
Default Alt Text
D59855.id.diff (9 KB)
Attached To
Mode
D59855: pmc: Generate calltree based on pmc samples
Attached
Detach File
Event Timeline
Log In to Comment