{"id":23658773,"url":"https://github.com/opencloudos/perf-prof","last_synced_at":"2025-04-06T12:08:18.670Z","repository":{"id":41519037,"uuid":"440453237","full_name":"OpenCloudOS/perf-prof","owner":"OpenCloudOS","description":"Kernel profiler based on perf_event and ebpf","archived":false,"fork":false,"pushed_at":"2025-03-26T07:22:44.000Z","size":15261,"stargazers_count":80,"open_issues_count":1,"forks_count":16,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-03-30T11:06:52.208Z","etag":null,"topics":["ebpf","linux","monitor","monitoring","perf","performance","profiling"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/OpenCloudOS.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.GPL2","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-12-21T09:03:54.000Z","updated_at":"2025-03-26T07:21:54.000Z","dependencies_parsed_at":"2023-10-26T09:43:55.839Z","dependency_job_id":"790cd79a-e230-4d88-9185-934dba25221c","html_url":"https://github.com/OpenCloudOS/perf-prof","commit_stats":null,"previous_names":[],"tags_count":37,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpenCloudOS%2Fperf-prof","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpenCloudOS%2Fperf-prof/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpenCloudOS%2Fperf-prof/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpenCloudOS%2Fperf-prof/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/OpenCloudOS","download_url":"https://codeload.github.com/OpenCloudOS/perf-prof/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247478321,"owners_count":20945266,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ebpf","linux","monitor","monitoring","perf","performance","profiling"],"created_at":"2024-12-29T00:50:58.158Z","updated_at":"2025-04-06T12:08:18.630Z","avatar_url":"https://github.com/OpenCloudOS.png","language":"C","readme":"# 基于perf/ebpf的分析框架\n\n基于`libperf`和`libtraceevent`库实现简单的分析框架，提供比perf更灵活的特性。\n\n- 数据不落盘，数据实时处理并输出。\n- 数据过滤，基于tracepoint的过滤机制，减少数据量。支持ebpf过滤器。\n- 兼容更多内核版本。\n- 用户态实现更安全，能快速迭代。\n\n虽然比perf更灵活，但不能替代perf。perf灵活的符号处理，支持大量的event，支持很多硬件PMU特性。\n\n![perf-prof框架](docs/images/perf-prof_framework.png)\n\n\n\n# 1 框架介绍\n\n整体框架由内核态和用户态 2 部分组成。\n\n## 1.1 内核态\n\n内核态分为几部分：事件源，filter，perf_event。\n\n内核态，事件源的事件经过`filter`过滤之后，存放到`ringbuffer`上，并递增`counter`计数器。\n\n***事件源***  目前有 5 种。\n\n- ebpf。bpf 程序可以调用 bpf_perf_event_output()直接往 perf 的 ringbuffer 内写入数据。\n\n- tracepoint。内核执行到 tracepoint 点的位置，就会采样事件。\n\n- kprobe。动态 tracepoint 点。其功能跟 tracepoint 点一致。\n\n- uprobe。跟 kprobe 类似，作用于用户态的二进制文件。\n\n- pmu。硬件事件源。源自 CPU 内部，PMC 计数溢出后触发采样。\n\n***filter*** 目前有3种。\n\n这三个过滤器，全是内核态过滤器，过滤出的事件才会放到 ringbuffer，被用户态使用。\n\n- ebpf 过滤器。可以在事件源 perf_event 上添加 bpf 程序。bpf 程序返回 1，可以继续采样；bpf 程序返回 0，终止采样。\n\n- trace event 过滤器。内核可以对 tracepoint 点的字段进行过滤。只过滤有满足条件的事件。\n\n- pmu 过滤器。仅支持 user、kernel 过滤。\n\n***perf_event*** 内包含 counter 和 ringbuffer 两部分。\n\n- counter，计数器，对事件发生次数进行计数。每个perf_event都有独立counter，不能共享。\n\n- ringbuffer，环形缓冲区，用于存放过滤后的事件。采样的事件格式，由 `perf_event_attr::sample_type `字段指定，包含基本的 CPU、pid、tid、时间戳、堆栈、raw、寄存器等信息。采样默认关闭，通过`perf_event_attr.sample_period`参数开启采样。每个perf_event都有独立的ringbuffer，多个perf_event可以共用ringbuffer。\n\n## 1.2 用户态\n\n**perf-prof** 框架不断读取`ringbuffer`的采样事件和`counter`，经过`order`排序事件，最后送到`profiler`处理事件。\n\n用户态分为 3 部分：基础功能、分析单元、联合分析。\n\n- 基础功能给分析单元提供基础服务。帮助系统、火焰图、符号解析、过滤器、order、expr、comm。\n- 分析单元：分成几个大类，每一个分析单元都是相互独立的，包含最小的分析功能。\n- 联合分析，把多个分析单元联合起来一起参与分析。\n\n# 2 分析单元\n\n分析单元分为一些大类：trace、计数分析、延迟分析、进程、内存、虚拟化、硬件与调试、块设备。\n\n每一个分析单元都是一个独立的 profiler(剖析器)。\n\n## 2.1 trace\n\ntrace 是最基本的分析单元。直接显示采样的事件，不做任何其他处理。一般用于 profiler 开发初期，直接观察事件的原始信息，并配合脚本处\n\n理事件。对于事件量比较少时，可以直观显示。\n\n## 2.2 计数分析\n\n1. stat。间隔输出事件的计数器。\n\n2. percpu-stat。精选好的一些事件，不需要指定。\n\n3. top。对事件的某些字段进行 top 分析。把字段的所有可能值拆分成独立的计数器，并按从大到小显示。\n\n4. hrcount。高精度计数器。可以观察到 ms 粒度事件的发生次数。用于事件发生密度分析，是否有一定的集中性。\n\n5. hrtimer。高精度条件采样。采样间隔内，事件发生一定次数时，输出采样的堆栈。\n\n6. num-dist。数值分布。事件的某字段的分布情况，最小值，平均值，p99，最大值。\n\n## 2.3 延迟分析\n\n1. multi-trace。多功能分析，主要用于事件延迟分析，并确定延迟的中间细节。\n\n2. syscalls。系统调用耗时分析。基于 multi-trace。只分析 sys_enter-\u003esys_exit 之间的延迟。\n\n3. nested-trace。嵌套的耗时分析。用于函数的发生关系分析，并统计每个函数的耗时。\n\n4. rundelay。调度延迟分析。基于 multi-trace，自动设置 filter。\n\n## 2.4 进程\n\n1. task-state。进程状态。可以统计进程 R，RD，S，D 等状态的分布情况。\n\n2. oncpu。进程运行在哪些 cpu 上。cpu 上运行过哪些进程。\n\n## 2.5 内存\n\n1. kmemleak。内存泄露分析。支持内核态多种内存分配器，以及用户态的内存分配器。\n\n2. kmemprof。内存分配热点分析。能够采集到哪些路径会密集分配内存。\n\n## 2.6 虚拟化\n\n1. kvm-exit。虚拟化退出耗时。\n\n2. kvmmmu。跟踪 mmu page 的分配和建立过程。\n\n## 2.7 块设备\n\n1. blktrace。跟踪块设备 request 在每个阶段的耗时。\n\n## 2.9 硬件与调试\n\n收集常用的硬件 PMU 事件。\n\n1. profile。指定频率采样。Cpu-cycles 事件。硬件 PMU 采样 NMI 中断，不会受到关中断影响。\n\n2. breakpoint。硬件断点。可以捕获对某个虚拟地址的读、写、执行。如：全局变量被修改。\n\n3. page-faults。缺页异常。跟踪系统发生的缺页异常。\n\n4. ldlat-loads。采样内存访问延迟。基于 PEBS。\n\n5. llcstat。L3 缓存状态。命中率。\n6. tlbstat。Tlb 状态。命中率。\n\n# 3 联合分析\n\n分析单元大致分成 2 类：\n\n- 内建事件分析单元：不需要使用-e 指定事件，一般使用 ebpf、硬件 pmu 作为内建事件源，也可以使用 tracepoint 作为事件源。\n\n- 指定事件分析单元：需要使用-e 等选项指定事件。\n\n对于 tracepoint、kprobe、uprobe 事件源可以使用 sys:name 表示，能够通过-e选项直接使用。对于 ebpf、pmu 事件源，没办法使用 sys:name 表示，其是封装在 profiler 内部，属于 profiler 的内建事件，因此这些 profiler 本身可以看做事件源。经过扩展，所有含有内建事件的 profiler，都可以看做事件源。\n\n联合分析，就是把这些 tracepoint、kprobe、uprobe 事件源，profiler 事件源产生的事件联合起来一起分析。\n\n- multi-trace 可以接受 trace、profile、task-state、breakpoint、page-faults 这几个一起参与延迟分析。\n- trace 可以接受 profile、task-state、breakpoint、page-faults 这几个一起排序后输出。\n\n# 4 基础功能\n\n## 4.1 模块化\n\n每个profiler都是独立的模块文件，可扩展，可裁减，损耗低。适合高性能监控场景。\n\n## 4.2 栈\n\n  - 栈及符号打印。可控制内核态、用户态、地址、符号、偏移量、dso、正向栈、反向栈，每个栈帧的分隔符、栈的分隔符。\n  - 支持解析内核符号(/proc/kallsyms)，用户态符号(.symtab/.dynsym)、MiniDebugInfo解析(.gnu_debugdata)。\n  - 支持debuginfo包。/usr/lib/debug/.build-id/\n  - key-value栈。以栈做为key，可以过滤重复栈，并能唯一寻址value。\n  - 生成火焰图折叠栈格式。\n\n## 4.3 用户态符号表\n\n用户态符号表，使用`syms_cache`结构表示，通过pid找到特定于进程的`syms`符号集合。\n\nsyms符号集合由/proc/pid/maps内所有的文件映射组成，每一个文件映射由一个`dso`来表示，syms包含dso的集合。\n\n每个dso由映射到进程地址空间内的[起始地址、结束地址、文件对象]表示。文件对象由`object`结构表示。\n\nobject结构表示一个动态库的符号集合，由多个`sym`组成。object是可以给多个进程共享的，通过引用计数管理object的引用和释放。\n\nsym表示一个特定的符号。由符号名字，起始地址，大小组成。\n\n```\nsyms_cache --\u003e syms --\u003e dso --\u003e object --\u003e sym\n```\n\n## 4.4 用户态内存泄露检测\n\n```\nLD_PRELOAD=/lib64/libtcmalloc.so HEAPCHECK=draconian PPROF_PATH=./perf-prof /path/to/bin\n```\n\n利用tcmalloc的内存泄露检测功能。\n\n- **LD_PRELOAD=**，预先加载tcmalloc库，替换glibc库的malloc和free函数。\n- **HEAPCHECK=**，内存泄露检测。draconian检测所有的内存泄露。\n- **PPROF_PATH=**，指定符号解析命令。`perf-prof --symbols`具备跟`pprof --symbols`一样的符号解析能力。\n\n## 4.5 栈的处理\n\n栈的处理方式各种各样，如perf top风格的栈处理，火焰图风格的栈处理。\n\nperf-prof目前支持的栈处理。\n\n- 栈及符号打印。用`callchain_ctx`表示，定义了栈的打印风格，可控制内核态、用户态、地址、符号、偏移量、dso、正向栈、反向栈。每个栈帧的分隔符、栈的分隔符。\n- key-value栈。以栈做为key，可以过滤重复栈，并能唯一寻址value。用`key_value_paires`结构表示，一般相同的栈都有类似的作用，如内存分配栈，可以分析相同的栈分配的总内存量，未释放的总内存量。类似于gperftools提供的HEAPCHECKE功能，最后报告的内存泄露是以栈为基准的。\n- 火焰图。把相同的栈以及栈的每一帧聚合到一起。用`flame_graph`结构表示，能够生成折叠栈格式：反向栈、每帧以\";\"分隔、末尾是栈的数量。例子：`swapper;start_kernel;rest_init;cpu_idle;default_idle;native_safe_halt 1`。使用[flamegraph.pl](https://github.com/brendangregg/FlameGraph/blob/master/flamegraph.pl)生成火焰图。\n\n## 4.6 火焰图\n\nperf-prof仅输出折叠栈格式，并对输出栈比较多的模块做了支持。目前已支持：`profile, task-state, kmemleak, trace`\n\n原先在stdout直接输出栈，目前切换成火焰图之后，不会再输出栈，而是会在命令结束时输出火焰图折叠栈文件。通过`[-g [--flame-graph file]]`参数启用火焰图，必须支持栈(-g)才能输出火焰图。折叠栈文件以`file.folded`命名。使用`flamegraph.pl`最终生成svg火焰图。\n\n```\n$ perf-prof task-state -S --than 100 --filter cat -g --flame-graph cat\n$ flamegraph.pl cat.folded \u003e cat.svg\n```\n\n### 4.6.1 按时间的火焰图\n\n是以固定间隔输出折叠栈，折叠栈包含时间戳。最终生成的火焰图是按时间排序的。对于长时间的监控，可以根据时间戳查找问题。\n\n```\n$ grep \"15:46:33\" cat.folded | flamegraph.pl \u003e cat.svg #生成15:46:33秒开始的火焰图\n```\n\n### 4.6.2 网络丢包火焰图\n\n```\n$ perf-prof trace -e skb:kfree_skb -g --flame-graph kfree_skb -m 128 #监控丢包\n$ perf-prof trace -e skb:kfree_skb -g --flame-graph kfree_skb -i 600000 -m 128 #每600秒间隔输出火焰图\n$ flamegraph.pl --reverse  kfree_skb.folded \u003e kfree_skb.svg #生成火焰图\n```\n\n### 4.6.3 CPU性能火焰图\n\n```\n$ perf-prof profile -F 1000 -C 0,1 --exclude-user -g --flame-graph profile #采样内核态CPU利用率的火焰图\n$ perf-prof profile -F 1000 -C 0,1 --exclude-user -g --flame-graph profile -i 600000 #每600秒间隔输出火焰图\n$ grep \"15:46:33\" profile.folded | flamegraph.pl \u003e profile.svg #生成15:46:33秒开始600秒的火焰图\n```\n\n## 4.7 延迟处理\n\nperf-prof目前支持的延迟处理。\n\n- 统计延迟。最大延迟，最小延迟，平均延迟。\n- 直方图。log2和linear直方图，使用`print_log2_hist`和`print_linear_hist`函数打印。\n- 热图。横坐标是时间轴，纵坐标是延迟信息。目前支持：`kvm-exit, multi-trace`\n\n## 4.8 热图\n\n```\n$ perf-prof multi-trace -e kvm:kvm_exit -e kvm:kvm_entry -C 1 --heatmap mpdelay\n$ trace2heatmap.pl --unitstime=ns --unitslabel=ns --grid mpdelay-kvm_exit-kvm_entry.lat \u003e mpdelay-kvm_exit-kvm_entry.svg\n```\n\n## 4.9 filter\n\n目前支持3类过滤器：ebpf过滤器、pmu过滤器、ftrace过滤器。\n\n通过`perf-prof -h`可以看到过滤器的选项：\n\n```\nEvent selector. use 'perf list tracepoint' to list available tp events.\n  EVENT,EVENT,...\n  EVENT: sys:name[/filter/ATTR/ATTR/.../]\n  filter: ftrace filter\n  ATTR:\n      ...\nFILTER OPTION:\n      --exclude-guest        exclude guest\n      --exclude-kernel       exclude kernel\n      --exclude-user         exclude user\n      --exclude_pid=PID      ebpf, exclude pid\n  -G, --exclude-host         Monitor GUEST, exclude host\n      --irqs_disabled[=0|1]  ebpf, irqs disabled or not.\n      --nr_running_max=N     ebpf, maximum number of running processes for CPU runqueue.\n      --nr_running_min=N     ebpf, minimum number of running processes for CPU runqueue.\n      --tif_need_resched[=0|1]   ebpf, TIF_NEED_RESCHED is set or not.\n```\n\n其中ebpf开头的是ebpf过滤器，其他的是pmu过滤器。ftrace过滤器，只能用于tracepoint事件。\n\n### 4.9.1 ebpf过滤器\n\n内核perf_event可以通过`ioctl(PERF_EVENT_IOC_SET_BPF)`来设置bpf程序。bpf程序返回1，可以继续采样；bpf程序返回0，终止采样。可以依据这样的策略，来给每个perf_event增加一个过滤器。过滤不需要的采样点。\n\n当前支持4个ebpf过滤器。\n\n- `--irqs_disabled`，判断中断是否关闭。`--irqs_disabled, --irqs_disabled=1`中断关闭继续采样，中断打开终止采样。`--irqs_disabled=0`中断打开继续采样，中断关闭终止采样。\n- `--tif_need_resched`，判断TIF_NEED_RESCHED标记是否设置。`--tif_need_resched, --tif_need_resched=1`标记设置继续采样，标记未设置终止采样。`--tif_need_resched=0`标记未设置继续采样，标记设置终止采样。\n- `--nr_running_min,--nr_running_max`，判断runqueue中nr_running进程的数量。`nr_running_min \u003c= nr_running \u003c= nr_running_max`条件满足继续采样，否则终止采样。\n- `--exclude_pid`，过滤掉进程pid。当前进程等于PID终止采样，否则继续采样。\n\n### 4.9.2 pmu过滤器\n\n内核perf框架默认会带一些简单的过滤器，主要是基于perf_event_attr属性来设置。\n\n当前支持4个pmu过滤器。\n\n- `--exclude-guest`，过滤掉guest模式。\n- `--exclude-host`，过滤掉host，只采样guest。一般用于硬件PMU。\n- `--exclude-kernel`，过滤掉内核态。\n- `--exclude-user`，过滤掉用户态。\n\n### 4.9.3 ftrace过滤器\n\n每个tracepoint事件都可以设置ftrace过滤器。\n\n```\n$ perf-prof trace -e 'sched:sched_stat_runtime help\n\nperf-prof trace -e \"sched:sched_stat_runtime/./[stack/]\" [-g] [--flame-graph .] [-C .] [-p .] [-i .] [--order] [-m .]\n\nsched:sched_stat_runtime\nname: sched_stat_runtime\nID: 237\nformat:\n        field:unsigned short common_type;       offset:0;       size:2; signed:0;\n        field:unsigned char common_flags;       offset:2;       size:1; signed:0;\n        field:unsigned char common_preempt_count;       offset:3;       size:1; signed:0;\n        field:int common_pid;   offset:4;       size:4; signed:1;\n\n        field:char comm[16];    offset:8;       size:16;        signed:1;\n        field:pid_t pid;        offset:24;      size:4; signed:1;\n        field:u64 runtime;      offset:32;      size:8; signed:0;\n        field:u64 vruntime;     offset:40;      size:8; signed:0;\n\nprint fmt: \"comm=%s pid=%d runtime=%Lu [ns] vruntime=%Lu [ns]\", REC-\u003ecomm, REC-\u003epid, (unsigned long long)REC-\u003eruntime, (unsigned long long)REC-\u003evruntime\n```\n\n通过在命令末尾加上`help`可以查看详细的帮助信息，其中包含tracepoint点的格式，可以找到可以作为过滤器的参数。\n\n```\nperf-prof trace -e 'sched:sched_stat_runtime/runtime\u003e1000000/'\n```\n\n过滤出`runtime\u003e1000000`的数据，放到ringbuffer，再由profiler进一步处理。\n\n## 4.10 Attach\n\nperf-prof 使用一些公共参数来控制perf_event附加到CPU、线程、cgroup上。\n\n```\nUsage: perf-prof [OPTION...] profiler [PROFILER OPTION...] [help] [cmd [args...]]\n OPTION:\n      --cgroups=cgroup,...   Attach to cgroups, support regular expression.\n  -C, --cpu=CPU[-CPU],...    Monitor the specified CPU, Dflt: all cpu\n  -p, --pids=PID,...         Attach to processes\n  -t, --tids=TID,...         Attach to threads\n```\n\n可以使用逗号分隔多个CPU、PID、TID、cgroup。\n\n### 4.10.1 Attach to CPU\n\n附加到CPU，只能监控指定的CPU上发生的事件。\n\nperf-prof trace -e sched:sched_stat_runtime `-C 0-1,3`\n\n### 4.10.2 Attach to PID/TID\n\n附加到PID/TID，只能监控指定的线程上发生的事件。\n\nperf-prof trace -e sched:sched_stat_runtime `-p 205835,205982`\n\nperf-prof trace -e sched:sched_stat_runtime `-t 205835,205982`\n\n附加到PID，会读取该pid下的所有线程，转换成附加到TID。\n\n### 4.10.3 Attach to workload\n\n附加到workload，监控workload执行过程中的事件。\n\n会通过fork、execvp来执行workload，并得到workload的pid。转换成附加到PID。\n\nperf-prof task-state `ip link show eth0`\n\n可以使用`--`强制分隔perf-prof的参数和workload的参数。\n\n### 4.10.4 Attach to cgroups\n\n附加到cgroups，监控cgroup内所有进程发生的事件。如果附加的PID太多，可以把这些PID放到perf_event cgroup内，附加到该cgroup，就能够监控到所有这些进程的事件。\n\n```bash\n# Example 1:\nmkdir /sys/fs/cgroup/perf_event/prof\necho 205835 \u003e /sys/fs/cgroup/perf_event/prof/tasks\ncat /proc/205835/cgroup | grep perf_event\n  5:perf_event:/prof\nperf-prof trace -e sched:sched_stat_runtime --cgroups 'prof' # prof\nperf-prof trace -e sched:sched_stat_runtime --cgroups 'pro*' # 正则表达式\n\n# Example 2:\nmkdir /sys/fs/cgroup/perf_event/prof1\necho 205845 \u003e /sys/fs/cgroup/perf_event/prof/tasks\nperf-prof trace -e sched:sched_stat_runtime --cgroups 'prof,prof1'\nperf-prof trace -e sched:sched_stat_runtime --cgroups 'prof*' # prof, prof1\n```\n\nperf_event cgroup 需要手动把需要观察的进程放进去。\n\ncgroup的指定相对于`/sys/fs/cgroup/perf_event/`目录，同时可以使用正则表达式，匹配多个perf_event cgroup。\n\n## 4.11 USDT\n\nusdt是用户态进程静态导出的trace点，编译之后存放在`.note.stapsdt`section中。解析该section，创建出uprobe就可以trace用户态执行。\n\n目前提供3个功能：\n\n- **list**，列出elf文件中的usdt。\n- **add**，利用usdt添加uprobe点，通过profider:name方式来使用。\n- **del**，删除已添加的uprobe点。\n\n```bash\n# Example:\nperf-prof usdt add libc:memory_malloc_retry@/usr/lib64/libc.so.6 -v\nperf-prof trace -e libc:memory_malloc_retry\n```\n\n当前已支持x86和arm64平台。\n\n[Exploring USDT Probes on Linux](https://leezhenghui.github.io/linux/2019/03/05/exploring-usdt-on-linux.html)\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopencloudos%2Fperf-prof","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopencloudos%2Fperf-prof","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopencloudos%2Fperf-prof/lists"}