prog.c (9f0ca0c1a50a7de5c71970aa452941199ed210d9) prog.c (47c09d6a9f6794caface4ad50930460b82d7c670)
1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2/* Copyright (C) 2017-2018 Netronome Systems, Inc. */
3
4#define _GNU_SOURCE
5#include <errno.h>
6#include <fcntl.h>
1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2/* Copyright (C) 2017-2018 Netronome Systems, Inc. */
3
4#define _GNU_SOURCE
5#include <errno.h>
6#include <fcntl.h>
7#include <signal.h>
7#include <stdarg.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <time.h>
12#include <unistd.h>
13#include <net/if.h>
8#include <stdarg.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <time.h>
13#include <unistd.h>
14#include <net/if.h>
15#include <sys/ioctl.h>
14#include <sys/types.h>
15#include <sys/stat.h>
16#include <sys/types.h>
17#include <sys/stat.h>
18#include <sys/syscall.h>
16
17#include <linux/err.h>
19
20#include <linux/err.h>
21#include <linux/perf_event.h>
18#include <linux/sizes.h>
19
20#include <bpf/bpf.h>
21#include <bpf/btf.h>
22#include <bpf/libbpf.h>
23
24#include "cfg.h"
25#include "main.h"

--- 1506 unchanged lines hidden (view full) ---

1532 return load_with_options(argc, argv, true);
1533}
1534
1535static int do_loadall(int argc, char **argv)
1536{
1537 return load_with_options(argc, argv, false);
1538}
1539
22#include <linux/sizes.h>
23
24#include <bpf/bpf.h>
25#include <bpf/btf.h>
26#include <bpf/libbpf.h>
27
28#include "cfg.h"
29#include "main.h"

--- 1506 unchanged lines hidden (view full) ---

1536 return load_with_options(argc, argv, true);
1537}
1538
1539static int do_loadall(int argc, char **argv)
1540{
1541 return load_with_options(argc, argv, false);
1542}
1543
1544#ifdef BPFTOOL_WITHOUT_SKELETONS
1545
1546static int do_profile(int argc, char **argv)
1547{
1548 return 0;
1549}
1550
1551#else /* BPFTOOL_WITHOUT_SKELETONS */
1552
1553#include "profiler.skel.h"
1554
1555struct profile_metric {
1556 const char *name;
1557 struct bpf_perf_event_value val;
1558 struct perf_event_attr attr;
1559 bool selected;
1560
1561 /* calculate ratios like instructions per cycle */
1562 const int ratio_metric; /* 0 for N/A, 1 for index 0 (cycles) */
1563 const char *ratio_desc;
1564 const float ratio_mul;
1565} metrics[] = {
1566 {
1567 .name = "cycles",
1568 .attr = {
1569 .type = PERF_TYPE_HARDWARE,
1570 .config = PERF_COUNT_HW_CPU_CYCLES,
1571 .exclude_user = 1,
1572 },
1573 },
1574 {
1575 .name = "instructions",
1576 .attr = {
1577 .type = PERF_TYPE_HARDWARE,
1578 .config = PERF_COUNT_HW_INSTRUCTIONS,
1579 .exclude_user = 1,
1580 },
1581 .ratio_metric = 1,
1582 .ratio_desc = "insns per cycle",
1583 .ratio_mul = 1.0,
1584 },
1585 {
1586 .name = "l1d_loads",
1587 .attr = {
1588 .type = PERF_TYPE_HW_CACHE,
1589 .config =
1590 PERF_COUNT_HW_CACHE_L1D |
1591 (PERF_COUNT_HW_CACHE_OP_READ << 8) |
1592 (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16),
1593 .exclude_user = 1,
1594 },
1595 },
1596 {
1597 .name = "llc_misses",
1598 .attr = {
1599 .type = PERF_TYPE_HW_CACHE,
1600 .config =
1601 PERF_COUNT_HW_CACHE_LL |
1602 (PERF_COUNT_HW_CACHE_OP_READ << 8) |
1603 (PERF_COUNT_HW_CACHE_RESULT_MISS << 16),
1604 .exclude_user = 1
1605 },
1606 .ratio_metric = 2,
1607 .ratio_desc = "LLC misses per million insns",
1608 .ratio_mul = 1e6,
1609 },
1610};
1611
1612static __u64 profile_total_count;
1613
1614#define MAX_NUM_PROFILE_METRICS 4
1615
1616static int profile_parse_metrics(int argc, char **argv)
1617{
1618 unsigned int metric_cnt;
1619 int selected_cnt = 0;
1620 unsigned int i;
1621
1622 metric_cnt = sizeof(metrics) / sizeof(struct profile_metric);
1623
1624 while (argc > 0) {
1625 for (i = 0; i < metric_cnt; i++) {
1626 if (is_prefix(argv[0], metrics[i].name)) {
1627 if (!metrics[i].selected)
1628 selected_cnt++;
1629 metrics[i].selected = true;
1630 break;
1631 }
1632 }
1633 if (i == metric_cnt) {
1634 p_err("unknown metric %s", argv[0]);
1635 return -1;
1636 }
1637 NEXT_ARG();
1638 }
1639 if (selected_cnt > MAX_NUM_PROFILE_METRICS) {
1640 p_err("too many (%d) metrics, please specify no more than %d metrics at at time",
1641 selected_cnt, MAX_NUM_PROFILE_METRICS);
1642 return -1;
1643 }
1644 return selected_cnt;
1645}
1646
1647static void profile_read_values(struct profiler_bpf *obj)
1648{
1649 __u32 m, cpu, num_cpu = obj->rodata->num_cpu;
1650 int reading_map_fd, count_map_fd;
1651 __u64 counts[num_cpu];
1652 __u32 key = 0;
1653 int err;
1654
1655 reading_map_fd = bpf_map__fd(obj->maps.accum_readings);
1656 count_map_fd = bpf_map__fd(obj->maps.counts);
1657 if (reading_map_fd < 0 || count_map_fd < 0) {
1658 p_err("failed to get fd for map");
1659 return;
1660 }
1661
1662 err = bpf_map_lookup_elem(count_map_fd, &key, counts);
1663 if (err) {
1664 p_err("failed to read count_map: %s", strerror(errno));
1665 return;
1666 }
1667
1668 profile_total_count = 0;
1669 for (cpu = 0; cpu < num_cpu; cpu++)
1670 profile_total_count += counts[cpu];
1671
1672 for (m = 0; m < ARRAY_SIZE(metrics); m++) {
1673 struct bpf_perf_event_value values[num_cpu];
1674
1675 if (!metrics[m].selected)
1676 continue;
1677
1678 err = bpf_map_lookup_elem(reading_map_fd, &key, values);
1679 if (err) {
1680 p_err("failed to read reading_map: %s",
1681 strerror(errno));
1682 return;
1683 }
1684 for (cpu = 0; cpu < num_cpu; cpu++) {
1685 metrics[m].val.counter += values[cpu].counter;
1686 metrics[m].val.enabled += values[cpu].enabled;
1687 metrics[m].val.running += values[cpu].running;
1688 }
1689 key++;
1690 }
1691}
1692
1693static void profile_print_readings_json(void)
1694{
1695 __u32 m;
1696
1697 jsonw_start_array(json_wtr);
1698 for (m = 0; m < ARRAY_SIZE(metrics); m++) {
1699 if (!metrics[m].selected)
1700 continue;
1701 jsonw_start_object(json_wtr);
1702 jsonw_string_field(json_wtr, "metric", metrics[m].name);
1703 jsonw_lluint_field(json_wtr, "run_cnt", profile_total_count);
1704 jsonw_lluint_field(json_wtr, "value", metrics[m].val.counter);
1705 jsonw_lluint_field(json_wtr, "enabled", metrics[m].val.enabled);
1706 jsonw_lluint_field(json_wtr, "running", metrics[m].val.running);
1707
1708 jsonw_end_object(json_wtr);
1709 }
1710 jsonw_end_array(json_wtr);
1711}
1712
1713static void profile_print_readings_plain(void)
1714{
1715 __u32 m;
1716
1717 printf("\n%18llu %-20s\n", profile_total_count, "run_cnt");
1718 for (m = 0; m < ARRAY_SIZE(metrics); m++) {
1719 struct bpf_perf_event_value *val = &metrics[m].val;
1720 int r;
1721
1722 if (!metrics[m].selected)
1723 continue;
1724 printf("%18llu %-20s", val->counter, metrics[m].name);
1725
1726 r = metrics[m].ratio_metric - 1;
1727 if (r >= 0 && metrics[r].selected &&
1728 metrics[r].val.counter > 0) {
1729 printf("# %8.2f %-30s",
1730 val->counter * metrics[m].ratio_mul /
1731 metrics[r].val.counter,
1732 metrics[m].ratio_desc);
1733 } else {
1734 printf("%-41s", "");
1735 }
1736
1737 if (val->enabled > val->running)
1738 printf("(%4.2f%%)",
1739 val->running * 100.0 / val->enabled);
1740 printf("\n");
1741 }
1742}
1743
1744static void profile_print_readings(void)
1745{
1746 if (json_output)
1747 profile_print_readings_json();
1748 else
1749 profile_print_readings_plain();
1750}
1751
1752static char *profile_target_name(int tgt_fd)
1753{
1754 struct bpf_prog_info_linear *info_linear;
1755 struct bpf_func_info *func_info;
1756 const struct btf_type *t;
1757 char *name = NULL;
1758 struct btf *btf;
1759
1760 info_linear = bpf_program__get_prog_info_linear(
1761 tgt_fd, 1UL << BPF_PROG_INFO_FUNC_INFO);
1762 if (IS_ERR_OR_NULL(info_linear)) {
1763 p_err("failed to get info_linear for prog FD %d", tgt_fd);
1764 return NULL;
1765 }
1766
1767 if (info_linear->info.btf_id == 0 ||
1768 btf__get_from_id(info_linear->info.btf_id, &btf)) {
1769 p_err("prog FD %d doesn't have valid btf", tgt_fd);
1770 goto out;
1771 }
1772
1773 func_info = (struct bpf_func_info *)(info_linear->info.func_info);
1774 t = btf__type_by_id(btf, func_info[0].type_id);
1775 if (!t) {
1776 p_err("btf %d doesn't have type %d",
1777 info_linear->info.btf_id, func_info[0].type_id);
1778 goto out;
1779 }
1780 name = strdup(btf__name_by_offset(btf, t->name_off));
1781out:
1782 free(info_linear);
1783 return name;
1784}
1785
1786static struct profiler_bpf *profile_obj;
1787static int profile_tgt_fd = -1;
1788static char *profile_tgt_name;
1789static int *profile_perf_events;
1790static int profile_perf_event_cnt;
1791
1792static void profile_close_perf_events(struct profiler_bpf *obj)
1793{
1794 int i;
1795
1796 for (i = profile_perf_event_cnt - 1; i >= 0; i--)
1797 close(profile_perf_events[i]);
1798
1799 free(profile_perf_events);
1800 profile_perf_event_cnt = 0;
1801}
1802
1803static int profile_open_perf_events(struct profiler_bpf *obj)
1804{
1805 unsigned int cpu, m;
1806 int map_fd, pmu_fd;
1807
1808 profile_perf_events = calloc(
1809 sizeof(int), obj->rodata->num_cpu * obj->rodata->num_metric);
1810 if (!profile_perf_events) {
1811 p_err("failed to allocate memory for perf_event array: %s",
1812 strerror(errno));
1813 return -1;
1814 }
1815 map_fd = bpf_map__fd(obj->maps.events);
1816 if (map_fd < 0) {
1817 p_err("failed to get fd for events map");
1818 return -1;
1819 }
1820
1821 for (m = 0; m < ARRAY_SIZE(metrics); m++) {
1822 if (!metrics[m].selected)
1823 continue;
1824 for (cpu = 0; cpu < obj->rodata->num_cpu; cpu++) {
1825 pmu_fd = syscall(__NR_perf_event_open, &metrics[m].attr,
1826 -1/*pid*/, cpu, -1/*group_fd*/, 0);
1827 if (pmu_fd < 0 ||
1828 bpf_map_update_elem(map_fd, &profile_perf_event_cnt,
1829 &pmu_fd, BPF_ANY) ||
1830 ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0)) {
1831 p_err("failed to create event %s on cpu %d",
1832 metrics[m].name, cpu);
1833 return -1;
1834 }
1835 profile_perf_events[profile_perf_event_cnt++] = pmu_fd;
1836 }
1837 }
1838 return 0;
1839}
1840
1841static void profile_print_and_cleanup(void)
1842{
1843 profile_close_perf_events(profile_obj);
1844 profile_read_values(profile_obj);
1845 profile_print_readings();
1846 profiler_bpf__destroy(profile_obj);
1847
1848 close(profile_tgt_fd);
1849 free(profile_tgt_name);
1850}
1851
1852static void int_exit(int signo)
1853{
1854 profile_print_and_cleanup();
1855 exit(0);
1856}
1857
1858static int do_profile(int argc, char **argv)
1859{
1860 int num_metric, num_cpu, err = -1;
1861 struct bpf_program *prog;
1862 unsigned long duration;
1863 char *endptr;
1864
1865 /* we at least need two args for the prog and one metric */
1866 if (!REQ_ARGS(3))
1867 return -EINVAL;
1868
1869 /* parse target fd */
1870 profile_tgt_fd = prog_parse_fd(&argc, &argv);
1871 if (profile_tgt_fd < 0) {
1872 p_err("failed to parse fd");
1873 return -1;
1874 }
1875
1876 /* parse profiling optional duration */
1877 if (argc > 2 && is_prefix(argv[0], "duration")) {
1878 NEXT_ARG();
1879 duration = strtoul(*argv, &endptr, 0);
1880 if (*endptr)
1881 usage();
1882 NEXT_ARG();
1883 } else {
1884 duration = UINT_MAX;
1885 }
1886
1887 num_metric = profile_parse_metrics(argc, argv);
1888 if (num_metric <= 0)
1889 goto out;
1890
1891 num_cpu = libbpf_num_possible_cpus();
1892 if (num_cpu <= 0) {
1893 p_err("failed to identify number of CPUs");
1894 goto out;
1895 }
1896
1897 profile_obj = profiler_bpf__open();
1898 if (!profile_obj) {
1899 p_err("failed to open and/or load BPF object");
1900 goto out;
1901 }
1902
1903 profile_obj->rodata->num_cpu = num_cpu;
1904 profile_obj->rodata->num_metric = num_metric;
1905
1906 /* adjust map sizes */
1907 bpf_map__resize(profile_obj->maps.events, num_metric * num_cpu);
1908 bpf_map__resize(profile_obj->maps.fentry_readings, num_metric);
1909 bpf_map__resize(profile_obj->maps.accum_readings, num_metric);
1910 bpf_map__resize(profile_obj->maps.counts, 1);
1911
1912 /* change target name */
1913 profile_tgt_name = profile_target_name(profile_tgt_fd);
1914 if (!profile_tgt_name)
1915 goto out;
1916
1917 bpf_object__for_each_program(prog, profile_obj->obj) {
1918 err = bpf_program__set_attach_target(prog, profile_tgt_fd,
1919 profile_tgt_name);
1920 if (err) {
1921 p_err("failed to set attach target\n");
1922 goto out;
1923 }
1924 }
1925
1926 set_max_rlimit();
1927 err = profiler_bpf__load(profile_obj);
1928 if (err) {
1929 p_err("failed to load profile_obj");
1930 goto out;
1931 }
1932
1933 err = profile_open_perf_events(profile_obj);
1934 if (err)
1935 goto out;
1936
1937 err = profiler_bpf__attach(profile_obj);
1938 if (err) {
1939 p_err("failed to attach profile_obj");
1940 goto out;
1941 }
1942 signal(SIGINT, int_exit);
1943
1944 sleep(duration);
1945 profile_print_and_cleanup();
1946 return 0;
1947
1948out:
1949 profile_close_perf_events(profile_obj);
1950 if (profile_obj)
1951 profiler_bpf__destroy(profile_obj);
1952 close(profile_tgt_fd);
1953 free(profile_tgt_name);
1954 return err;
1955}
1956
1957#endif /* BPFTOOL_WITHOUT_SKELETONS */
1958
1540static int do_help(int argc, char **argv)
1541{
1542 if (json_output) {
1543 jsonw_null(json_wtr);
1544 return 0;
1545 }
1546
1547 fprintf(stderr,

--- 7 unchanged lines hidden (view full) ---

1555 " [pinmaps MAP_DIR]\n"
1556 " %s %s attach PROG ATTACH_TYPE [MAP]\n"
1557 " %s %s detach PROG ATTACH_TYPE [MAP]\n"
1558 " %s %s run PROG \\\n"
1559 " data_in FILE \\\n"
1560 " [data_out FILE [data_size_out L]] \\\n"
1561 " [ctx_in FILE [ctx_out FILE [ctx_size_out M]]] \\\n"
1562 " [repeat N]\n"
1959static int do_help(int argc, char **argv)
1960{
1961 if (json_output) {
1962 jsonw_null(json_wtr);
1963 return 0;
1964 }
1965
1966 fprintf(stderr,

--- 7 unchanged lines hidden (view full) ---

1974 " [pinmaps MAP_DIR]\n"
1975 " %s %s attach PROG ATTACH_TYPE [MAP]\n"
1976 " %s %s detach PROG ATTACH_TYPE [MAP]\n"
1977 " %s %s run PROG \\\n"
1978 " data_in FILE \\\n"
1979 " [data_out FILE [data_size_out L]] \\\n"
1980 " [ctx_in FILE [ctx_out FILE [ctx_size_out M]]] \\\n"
1981 " [repeat N]\n"
1982 " %s %s profile PROG [duration DURATION] METRICs\n"
1563 " %s %s tracelog\n"
1564 " %s %s help\n"
1565 "\n"
1566 " " HELP_SPEC_MAP "\n"
1567 " " HELP_SPEC_PROGRAM "\n"
1568 " TYPE := { socket | kprobe | kretprobe | classifier | action |\n"
1569 " tracepoint | raw_tracepoint | xdp | perf_event | cgroup/skb |\n"
1570 " cgroup/sock | cgroup/dev | lwt_in | lwt_out | lwt_xmit |\n"
1571 " lwt_seg6local | sockops | sk_skb | sk_msg | lirc_mode2 |\n"
1572 " sk_reuseport | flow_dissector | cgroup/sysctl |\n"
1573 " cgroup/bind4 | cgroup/bind6 | cgroup/post_bind4 |\n"
1574 " cgroup/post_bind6 | cgroup/connect4 | cgroup/connect6 |\n"
1575 " cgroup/sendmsg4 | cgroup/sendmsg6 | cgroup/recvmsg4 |\n"
1576 " cgroup/recvmsg6 | cgroup/getsockopt | cgroup/setsockopt |\n"
1577 " struct_ops | fentry | fexit | freplace }\n"
1578 " ATTACH_TYPE := { msg_verdict | stream_verdict | stream_parser |\n"
1579 " flow_dissector }\n"
1983 " %s %s tracelog\n"
1984 " %s %s help\n"
1985 "\n"
1986 " " HELP_SPEC_MAP "\n"
1987 " " HELP_SPEC_PROGRAM "\n"
1988 " TYPE := { socket | kprobe | kretprobe | classifier | action |\n"
1989 " tracepoint | raw_tracepoint | xdp | perf_event | cgroup/skb |\n"
1990 " cgroup/sock | cgroup/dev | lwt_in | lwt_out | lwt_xmit |\n"
1991 " lwt_seg6local | sockops | sk_skb | sk_msg | lirc_mode2 |\n"
1992 " sk_reuseport | flow_dissector | cgroup/sysctl |\n"
1993 " cgroup/bind4 | cgroup/bind6 | cgroup/post_bind4 |\n"
1994 " cgroup/post_bind6 | cgroup/connect4 | cgroup/connect6 |\n"
1995 " cgroup/sendmsg4 | cgroup/sendmsg6 | cgroup/recvmsg4 |\n"
1996 " cgroup/recvmsg6 | cgroup/getsockopt | cgroup/setsockopt |\n"
1997 " struct_ops | fentry | fexit | freplace }\n"
1998 " ATTACH_TYPE := { msg_verdict | stream_verdict | stream_parser |\n"
1999 " flow_dissector }\n"
2000 " METRIC := { cycles | instructions | l1d_loads | llc_misses }\n"
1580 " " HELP_SPEC_OPTIONS "\n"
1581 "",
1582 bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
1583 bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
1584 bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
2001 " " HELP_SPEC_OPTIONS "\n"
2002 "",
2003 bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
2004 bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
2005 bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
1585 bin_name, argv[-2]);
2006 bin_name, argv[-2], bin_name, argv[-2]);
1586
1587 return 0;
1588}
1589
1590static const struct cmd cmds[] = {
1591 { "show", do_show },
1592 { "list", do_show },
1593 { "help", do_help },
1594 { "dump", do_dump },
1595 { "pin", do_pin },
1596 { "load", do_load },
1597 { "loadall", do_loadall },
1598 { "attach", do_attach },
1599 { "detach", do_detach },
1600 { "tracelog", do_tracelog },
1601 { "run", do_run },
2007
2008 return 0;
2009}
2010
2011static const struct cmd cmds[] = {
2012 { "show", do_show },
2013 { "list", do_show },
2014 { "help", do_help },
2015 { "dump", do_dump },
2016 { "pin", do_pin },
2017 { "load", do_load },
2018 { "loadall", do_loadall },
2019 { "attach", do_attach },
2020 { "detach", do_detach },
2021 { "tracelog", do_tracelog },
2022 { "run", do_run },
2023 { "profile", do_profile },
1602 { 0 }
1603};
1604
1605int do_prog(int argc, char **argv)
1606{
1607 return cmd_select(cmds, argc, argv, do_help);
1608}
2024 { 0 }
2025};
2026
2027int do_prog(int argc, char **argv)
2028{
2029 return cmd_select(cmds, argc, argv, do_help);
2030}