diff --git a/devel/perfetto/Makefile b/devel/perfetto/Makefile index 88c25e8f9682..a787bc000ce9 100644 --- a/devel/perfetto/Makefile +++ b/devel/perfetto/Makefile @@ -1,71 +1,75 @@ PORTNAME= perfetto DISTVERSIONPREFIX= v -DISTVERSION= 54.0 +DISTVERSION= 57.2 CATEGORIES= devel - -MAINTAINER= siva@FreeBSD.org -COMMENT= System profiling, app tracing, and trace analysis -WWW= https://perfetto.dev/ - MASTER_SITES= https://storage.googleapis.com/perfetto/:perfetto DISTFILES= sqlite-amalgamation-3500300.zip:perfetto \ llvm-project-617a15a9eac96088ae5e9134248d8236e34b91b1.tgz:perfetto LICENSE= APACHE20 LICENSE_FILE= ${WRKSRC}/LICENSE -PATCH_SITES= https://github.com/google/perfetto/commit/:upstream \ - https://codeberg.org/tj/perfetto/commit/:tj -PATCHFILES= 0a47f21e97615e0259dcb8589190aa0912c47bbe.patch:upstream \ - d400ae17f63c91846fb489dc716109fbfac311a4.patch:tj \ - 0e50ef0b9372580b18c2693a1f4a95cb8ee8abc7.patch:tj \ - 6620157cb2a8cf3334219fce26cd91d8089a8cd2.patch:tj \ - 1b24730762d082dae073f096d5e666e0fc83a1bb.patch:tj - -PATCH_DIST_STRIP= -p1 +MAINTAINER= siva@FreeBSD.org +COMMENT= System profiling, app tracing, and trace analysis +WWW= https://perfetto.dev/ BUILD_DEPENDS= gn:devel/gn USES= compiler:c++17-lang ninja python:build +OPTIONS_DEFINE= EXAMPLES +OPTIONS_DEFAULT= EXAMPLES +OPTIONS_SUB= YES + USE_GITHUB= yes GH_ACCOUNT= google +# Taken from tools/install-build-deps GH_TUPLE= protocolbuffers:protobuf:v31.1:buildtools_protobuf/buildtools/protobuf \ abseil:abseil-cpp:20250512.1:buildtools_abseil_cpp/buildtools/abseil-cpp \ sqlite:sqlite:version-3.50.3:buildtools_sqlite_src/buildtools/sqlite_src \ + google:re2:927f5d53caf8111721e734cf24724686bb745f55:buildtools_re2/buildtools/re2 \ libexpat:libexpat:R_2_6_2:buildtools_expat/buildtools/expat/src GN_ARGS= is_debug=false \ skip_buildtools_check=true \ perfetto_use_system_zlib=true \ extra_cflags="-I${LOCALBASE}/include -Wno-unused-parameter" \ extra_ldflags="-L${LOCALBASE}/lib" BINARY_ALIAS= python3=${PYTHON_CMD} BUILD_WRKSRC= ${WRKSRC}/out -ALL_TARGET= dtrace_probe freebsd_power freebsd_sys libperfetto_c.so perfetto tracebox traceconv traced traced_relay +_PERFETTO_TARGETS= perfetto tracebox traceconv traced traced_relay +_FREEBSD_TARGETS= dtrace_probe freebsd_power freebsd_sys +ALL_TARGET= libperfetto.a libperfetto_c.so ${_PERFETTO_TARGETS} ${_FREEBSD_TARGETS} post-extract: ${MV} ${WRKDIR}/sqlite-amalgamation-3500300 ${WRKSRC}/buildtools/sqlite ${MV} ${WRKDIR}/llvm-project ${WRKSRC}/buildtools/llvm-project do-configure: cd ${WRKSRC} && ${SETENV} ${CONFIGURE_ENV} gn gen out --args='${GN_ARGS}' && ${SETENV} ${CONFIGURE_ENV} gn ls out --args='${GN_ARGS}' cd ${WRKSRC} && ${SETENV} ${CONFIGURE_ENV} tools/gen_c_protos \ --proto protos/perfetto/trace/power/power_rails.proto \ --proto protos/perfetto/trace/power/battery_counters.proto \ --proto protos/perfetto/trace/system_info/cpu_info.proto \ --proto protos/perfetto/trace/sys_stats/sys_stats.proto \ --proto protos/perfetto/common/sys_stats_counters.proto \ ${BUILD_WRKSRC} do-install: - cd ${BUILD_WRKSRC} && ${INSTALL_PROGRAM} \ - dtrace_probe freebsd_power freebsd_sys perfetto tracebox traceconv traced traced_relay \ - ${STAGEDIR}${PREFIX}/bin ${INSTALL_PROGRAM} ${BUILD_WRKSRC}/libperfetto_c.so ${STAGEDIR}${PREFIX}/lib ${INSTALL_DATA} ${BUILD_WRKSRC}/libperfetto.a ${STAGEDIR}${PREFIX}/lib +.for b in ${_PERFETTO_TARGETS} + ${INSTALL_PROGRAM} ${BUILD_WRKSRC}/${b} ${STAGEDIR}${PREFIX}/bin +.endfor +.for b in ${_FREEBSD_TARGETS} + ${INSTALL_PROGRAM} ${BUILD_WRKSRC}/${b} ${STAGEDIR}${PREFIX}/bin/perfetto_${b} +.endfor + +post-install-EXAMPLES-on: + ${MKDIR} ${STAGEDIR}${EXAMPLESDIR} + cd ${WRKSRC}/examples && ${COPYTREE_SHARE} . ${STAGEDIR}${EXAMPLESDIR} .include diff --git a/devel/perfetto/distinfo b/devel/perfetto/distinfo index 35fd277f71d6..23c7606e2da6 100644 --- a/devel/perfetto/distinfo +++ b/devel/perfetto/distinfo @@ -1,25 +1,17 @@ -TIMESTAMP = 1783544597 +TIMESTAMP = 1783610442 SHA256 (sqlite-amalgamation-3500300.zip) = 9ad6d16cbc1df7cd55c8b55127c82a9bca5e9f287818de6dc87e04e73599d754 SIZE (sqlite-amalgamation-3500300.zip) = 2845433 SHA256 (llvm-project-617a15a9eac96088ae5e9134248d8236e34b91b1.tgz) = 7e2541446a27f2a09a84520da7bc93cd71749ba0f17318f2d5291fbf45b97956 SIZE (llvm-project-617a15a9eac96088ae5e9134248d8236e34b91b1.tgz) = 83475 -SHA256 (google-perfetto-v54.0_GH0.tar.gz) = 8a6144d81592e00fb9e75da42483a2544315b25e3113845505d69950083913c1 -SIZE (google-perfetto-v54.0_GH0.tar.gz) = 49105061 +SHA256 (google-perfetto-v57.2_GH0.tar.gz) = 99469718d2e531bff7a33c7ee03a99ddc0f3710be5b4f744bd41d3794df274ab +SIZE (google-perfetto-v57.2_GH0.tar.gz) = 58661586 SHA256 (protocolbuffers-protobuf-v31.1_GH0.tar.gz) = c3a0a9ece8932e31c3b736e2db18b1c42e7070cd9b881388b26d01aa71e24ca2 SIZE (protocolbuffers-protobuf-v31.1_GH0.tar.gz) = 9602477 SHA256 (abseil-abseil-cpp-20250512.1_GH0.tar.gz) = 9b7a064305e9fd94d124ffa6cc358592eb42b5da588fb4e07d09254aa40086db SIZE (abseil-abseil-cpp-20250512.1_GH0.tar.gz) = 2221441 SHA256 (sqlite-sqlite-version-3.50.3_GH0.tar.gz) = ced5a0e1928b8851f5f2176a521241ac42746233b5cd02341c1d9fa40d5ca222 SIZE (sqlite-sqlite-version-3.50.3_GH0.tar.gz) = 12992292 +SHA256 (google-re2-927f5d53caf8111721e734cf24724686bb745f55_GH0.tar.gz) = 8635bc46ac8d73974b4198229805287c8d620245f2081af155d7d96d4988a3a5 +SIZE (google-re2-927f5d53caf8111721e734cf24724686bb745f55_GH0.tar.gz) = 397888 SHA256 (libexpat-libexpat-R_2_6_2_GH0.tar.gz) = fbd032683370d761ba68dba2566d3280a154f5290634172d60a79b24d366d9dc SIZE (libexpat-libexpat-R_2_6_2_GH0.tar.gz) = 8416128 -SHA256 (0a47f21e97615e0259dcb8589190aa0912c47bbe.patch) = 7aa9dfbe2ee1509b1253859681b34efa135419c51a9f17be817e6e0735d531d5 -SIZE (0a47f21e97615e0259dcb8589190aa0912c47bbe.patch) = 1432 -SHA256 (d400ae17f63c91846fb489dc716109fbfac311a4.patch) = 6881cd5790888fedb4f8894c1cccd21e9b3876249ef0823a77593af0fdcbb0e7 -SIZE (d400ae17f63c91846fb489dc716109fbfac311a4.patch) = 20290 -SHA256 (0e50ef0b9372580b18c2693a1f4a95cb8ee8abc7.patch) = bb89d2358c9299b5c4a387f685565c20ef878b5eb476f211350f7d226a8b5a3d -SIZE (0e50ef0b9372580b18c2693a1f4a95cb8ee8abc7.patch) = 19299 -SHA256 (6620157cb2a8cf3334219fce26cd91d8089a8cd2.patch) = 4a0929ceea5ac478d69e56282100c44f839add744ccbcf81c2ee92a1847c84b4 -SIZE (6620157cb2a8cf3334219fce26cd91d8089a8cd2.patch) = 2159 -SHA256 (1b24730762d082dae073f096d5e666e0fc83a1bb.patch) = 085452de9e97cfe272f6bc0ca6d8842009f2b6ce7ad08eaa9e8b8e40971185c5 -SIZE (1b24730762d082dae073f096d5e666e0fc83a1bb.patch) = 22460 diff --git a/devel/perfetto/files/patch-BUILD.gn b/devel/perfetto/files/patch-BUILD.gn new file mode 100644 index 000000000000..5f6d418c27d2 --- /dev/null +++ b/devel/perfetto/files/patch-BUILD.gn @@ -0,0 +1,12 @@ +--- BUILD.gn.orig 2026-07-07 14:09:45 UTC ++++ BUILD.gn +@@ -140,6 +140,9 @@ if (enable_perfetto_integration_tests) { + all_targets += [ + "examples/shared_lib:example_shlib_data_source", + "examples/shared_lib:example_shlib_track_event", ++ "examples/dtrace:dtrace_probe", ++ "examples/freebsd:freebsd_power", ++ "examples/freebsd:freebsd_sys", + ] + } + } diff --git a/devel/perfetto/files/patch-examples_dtrace_BUILD.gn b/devel/perfetto/files/patch-examples_dtrace_BUILD.gn new file mode 100644 index 000000000000..c69806f9616d --- /dev/null +++ b/devel/perfetto/files/patch-examples_dtrace_BUILD.gn @@ -0,0 +1,36 @@ +--- examples/dtrace/BUILD.gn.orig 2026-07-09 15:43:01 UTC ++++ examples/dtrace/BUILD.gn +@@ -0,0 +1,33 @@ ++# Copyright (C) 2022 The Android Open Source Project ++# ++# Licensed under the Apache License, Version 2.0 (the "License"); ++# you may not use this file except in compliance with the License. ++# You may obtain a copy of the License at ++# ++# http://www.apache.org/licenses/LICENSE-2.0 ++# ++# Unless required by applicable law or agreed to in writing, software ++# distributed under the License is distributed on an "AS IS" BASIS, ++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++# See the License for the specific language governing permissions and ++# limitations under the License. ++ ++# On windows C11 (required for atomics) is not really supported, so we can't ++# really build a C executable. ++assert(!is_win) ++ ++executable("dtrace_probe") { ++ testonly = true ++ deps = [ ++ "../../gn:default_deps", ++ "../../src/shared_lib:libperfetto_c", ++ ] ++ libs = [ ++ "dtrace", ++ "sbuf", ++ ] ++ sources = [ ++ "dtrace_probe.c", ++ ] ++} ++ diff --git a/devel/perfetto/files/patch-examples_dtrace_dtrace__probe.c b/devel/perfetto/files/patch-examples_dtrace_dtrace__probe.c new file mode 100644 index 000000000000..4b188c026bcf --- /dev/null +++ b/devel/perfetto/files/patch-examples_dtrace_dtrace__probe.c @@ -0,0 +1,416 @@ +--- examples/dtrace/dtrace_probe.c.orig 2026-07-09 15:43:01 UTC ++++ examples/dtrace/dtrace_probe.c +@@ -0,0 +1,413 @@ ++/* ++ * Copyright (C) 2026 The FreeBSD Foundation ++ * ++ * Portions of this file were written by Tom Jones (thj@freebsd.org) under ++ * sponsorship from The FreeBSD Foundation. ++ * ++ * Licensed under the Apache License, Version 2.0 (the "License"); ++ * you may not use this file except in compliance with the License. ++ * You may obtain a copy of the License at ++ * ++ * http://www.apache.org/licenses/LICENSE-2.0 ++ * ++ * Unless required by applicable law or agreed to in writing, software ++ * distributed under the License is distributed on an "AS IS" BASIS, ++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ * See the License for the specific language governing permissions and ++ * limitations under the License. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "perfetto/public/data_source.h" ++#include "perfetto/public/producer.h" ++#include "perfetto/public/pb_decoder.h" ++//#include "perfetto/public/protos/trace/system_info/cpu_info.pbzero.h" ++//#include "perfetto/public/protos/trace/sys_stats/sys_stats.pbzero.h" ++ ++#include "perfetto/public/track_event.h" ++ ++#define DTRACE_CONFIG 143 ++#define DTRACE_CONFIG_FBT_PROBE 1 ++#define DTRACE_CONFIG_KINST_PROBE 2 ++ ++struct dtrace_context { ++ dtrace_hdl_t *g_dtp; ++ struct sbuf *program_text; ++ dtrace_prog_t *program; ++ struct PerfettoTeRegisteredTrack *tracks; ++ bool setupandrunning; ++ uint32_t fbt_events; ++ uint32_t kinst_events; ++}; ++ ++static struct PerfettoDs custom = PERFETTO_DS_INIT(); ++ ++static uint64_t get_current_time_ns(void) { ++ struct timespec ts; ++ ++ (void)clock_gettime(CLOCK_BOOTTIME, &ts); ++ ++ return (((uint64_t)ts.tv_sec * 1000000000ULL) + ts.tv_nsec); ++} ++ ++static void ++dfail(dtrace_hdl_t *g_dtp, const char *message, ...) ++{ ++ va_list args; ++ va_start(args, message); ++ (void) fprintf(stderr, "dtrace_probe: "); ++ (void) vfprintf(stderr, message, args); ++ va_end(args); ++ (void) fprintf(stderr, ": %s %d\n", ++ dtrace_errmsg(g_dtp, dtrace_errno(g_dtp)), dtrace_errno(g_dtp)); ++ exit(2); ++} ++ ++static void * dtrace_on_setup_cb(struct PerfettoDsImpl *impl, ++ PerfettoDsInstanceIndex inst_id, void *ds_config, ++ size_t ds_config_size, void* user_arg, struct PerfettoDsOnSetupArgs *args) { ++ ++ (void)impl; ++ (void)inst_id; ++ (void)args; ++ ++ int err; ++ struct dtrace_context *dctx = (struct dtrace_context *)user_arg; ++ int probes = 0; ++ ++ dctx->program_text = sbuf_new_auto(); ++ for (struct PerfettoPbDecoderIterator it = ++ PerfettoPbDecoderIterateBegin(ds_config, ds_config_size); ++ it.field.status == PERFETTO_PB_DECODER_OK; ++ PerfettoPbDecoderIterateNext(&it)) { ++ if (it.field.wire_type == 2 && it.field.id == DTRACE_CONFIG) { ++ struct PerfettoPbDecoderDelimitedField *dval = &it.field.value.delimited; ++ ++ for (struct PerfettoPbDecoderIterator dit = ++ PerfettoPbDecoderIterateNestedBegin(*dval); ++ dit.field.status == PERFETTO_PB_DECODER_OK; ++ PerfettoPbDecoderIterateNext(&dit)) { ++ if (dit.field.wire_type == 2) { ++ ++ struct PerfettoPbDecoderDelimitedField *p = &dit.field.value.delimited; ++ switch (dit.field.id) { ++ case DTRACE_CONFIG_FBT_PROBE: ++ ++ /* entry probe */ ++ sbuf_printf(dctx->program_text, "fbt:kernel:"); ++ sbuf_bcat(dctx->program_text, p->start, p->len); ++ sbuf_printf(dctx->program_text, ":entry\n"); ++ ++ probes++; ++ ++ /* return probe */ ++ sbuf_printf(dctx->program_text, "fbt:kernel:"); ++ sbuf_bcat(dctx->program_text, p->start, p->len); ++ sbuf_printf(dctx->program_text, ":return\n"); ++ ++ probes++; ++ break; ++ case DTRACE_CONFIG_KINST_PROBE: ++ ++ /* ++ * instruction probe ++ * This can be a blank cheque ++ * to add inst probes to the ++ * kernel, but I can't see an ++ * option that is less flexible ++ * being useful. ++ */ ++ sbuf_printf(dctx->program_text, "kinst::"); ++ sbuf_bcat(dctx->program_text, p->start, p->len); ++ sbuf_printf(dctx->program_text, "\n"); ++ ++ probes++; ++ ++ break; ++ default: ++ printf("unsupported config type %d!\n", dit.field.id); ++ break; ++ } ++ } ++ } ++ } ++ } ++ sbuf_finish(dctx->program_text); ++ printf("Installed %d probes\n", probes); ++ printf("---------- dtrace program --------------------\n"); ++ sbuf_putbuf(dctx->program_text); ++ printf("----------------------------------------------\n\n"); ++ ++ if ((dctx->g_dtp = dtrace_open(DTRACE_VERSION, 0, &err)) == NULL) { ++ printf("failed to initialize dtrace: %s\n", ++ dtrace_errmsg(NULL, err)); ++ exit(1); ++ } ++ ++ /* ++ * If we don't set a bufsize we will immediately get a failure due to ++ * buffer exhaustion. ++ */ ++ if (dtrace_setopt(dctx->g_dtp, "bufsize", "10M") == -1) ++ dfail(dctx->g_dtp, "failed to set 'bufsize'"); ++ ++ if (dtrace_setopt(dctx->g_dtp, "bufpolicy", "switch") == -1) ++ dfail(dctx->g_dtp, "failed to set buffer policy 'switch'"); ++ ++ if ((dctx->program = dtrace_program_strcompile(dctx->g_dtp, ++ sbuf_data(dctx->program_text), DTRACE_PROBESPEC_NAME, 0, 0, NULL)) == NULL) { ++ dfail(dctx->g_dtp, "failed to compile program"); ++ exit(1); ++ } ++ ++ dtrace_proginfo_t info; ++ if (dtrace_program_exec(dctx->g_dtp, dctx->program, &info) == -1) { ++ dfail(dctx->g_dtp, "failed to enable probes"); ++ exit(1); ++ } ++ ++ if (dtrace_go(dctx->g_dtp) != 0) { ++ dfail(dctx->g_dtp, "couldn't start tracing\n"); ++ exit(1); ++ } ++ ++ /* ++ * Set up a track for each cpu . ++ * XXX: TODO hardcode my cores (nproc=16) ++ */ ++ struct PerfettoTeRegisteredTrack *cpu_tracks = ++ calloc(sizeof(struct PerfettoTeRegisteredTrack), 16); ++ dctx->tracks = cpu_tracks; ++ for (int i = 0; i < 16; i++) { ++ ++ struct sbuf *name = sbuf_new_auto(); ++ ++ sbuf_printf(name, "CPU %d", i); ++ sbuf_finish(name); ++// printf("registering uuid \t%s\n", sbuf_data(name)); ++ PerfettoTeNamedTrackRegister(cpu_tracks, sbuf_data(name), ++ i, PerfettoTeGlobalTrackUuid(), true); ++// printf("uuid %lx\n", cpu_tracks->impl.uuid); ++ sbuf_delete(name); ++ cpu_tracks++; ++ } ++ ++ ++ /* ++ * XXX: We end up with both inst_ctx and user arg being the same struct ++ * dtrace_context *. ++ */ ++ return (void *)dctx; ++ ++} ++ ++static void dtrace_on_start_cb(struct PerfettoDsImpl* impl, ++ PerfettoDsInstanceIndex inst_id, void* user_arg, ++ void* inst_ctx, struct PerfettoDsOnStartArgs* args) { ++ ++ (void)impl; ++ (void)inst_id; ++ (void)inst_ctx; ++ (void)args; ++ ++ struct dtrace_context *dctx = (struct dtrace_context *)user_arg; ++ dctx->setupandrunning = true; ++} ++ ++static void dtrace_on_stop_cb(struct PerfettoDsImpl *impl, ++ PerfettoDsInstanceIndex inst_id, void* user_arg, void* inst_ctx, ++ struct PerfettoDsOnStopArgs* args) { ++ ++ (void)impl; ++ (void)inst_id; ++ (void)user_arg; ++ (void)inst_ctx; ++ (void)args; ++ ++ ++ struct dtrace_context *dctx = (struct dtrace_context *)user_arg; ++ ++ printf("%s\tfbt events: %u kinst events: %u\n", __func__, dctx->fbt_events, dctx->kinst_events); ++ ++ dctx->setupandrunning = false; ++ dtrace_stop(dctx->g_dtp); ++ ++ dctx->program = NULL; ++ sbuf_delete(dctx->program_text); ++ ++ struct PerfettoTeRegisteredTrack *tracks = dctx->tracks; ++ for (int i = 0; i < 16; i++) { ++ PerfettoTeRegisteredTrackUnregister(tracks); ++ tracks++; ++ } ++ ++ free(dctx->tracks); ++ dctx->tracks = NULL; ++ ++} ++ ++static void dtrace_on_destroy_cb(struct PerfettoDsImpl *impl, ++ void* user_arg, void* inst_ctx) { ++ ++ (void)impl; ++ (void)user_arg; ++ (void)inst_ctx; ++ ++ printf("%s\n", __func__); ++} ++ ++static int ++process_record(const dtrace_probedata_t *data, const dtrace_recdesc_t *rec, void *arg) ++{ ++ (void)rec; ++ ++ struct dtrace_context *dctx = (struct dtrace_context *)arg; ++ ++ dtrace_probedesc_t *pd = data->dtpda_pdesc; ++ processorid_t cpu = data->dtpda_cpu; ++ uint64_t track_uuid; ++#if 0 ++ printf("%3d uuid 0x%lx ts %zd str %s %s:%s\n", ++ cpu, track_uuid, data->dtpda_timestamp, ++ sbuf_data(tn), pd->dtpd_func, pd->dtpd_name); ++#endif ++ //uintptr_t addr; ++#if 0 ++ dtrace_actkind_t act; ++ if (rec == NULL) { ++ return (DTRACE_CONSUME_NEXT); ++ } ++ act = rec->dtrd_action; ++printf("%s:%d\n", __func__, __LINE__); ++ //addr = (uintptr_t)data->dtpda_data; ++ if (act == DTRACEACT_EXIT) { ++ //g_status = *((uint32_t *)addr); ++ return (DTRACE_CONSUME_NEXT); ++ } ++#endif ++ PERFETTO_DS_TRACE(custom, ctx) { ++ struct PerfettoDsRootTracePacket root; ++ PerfettoDsTracerPacketBegin(&ctx, &root); ++ ++ /* XXX: this is a free running nanotime counter from "some point in time" */ ++ perfetto_protos_TracePacket_set_timestamp(&root.msg, get_current_time_ns()); ++ ++ perfetto_protos_TracePacket_set_timestamp(&root.msg, data->dtpda_timestamp); ++ //perfetto_protos_TracePacket_set_timestamp_clock_id(&root.msg, XXX); ++ ++ { ++ struct perfetto_protos_TrackEvent te; ++ perfetto_protos_TracePacket_begin_track_event(&root.msg, &te); ++ ++ /* Set track name */ ++ /* XXX: TODO This should be an internened track name */ ++ struct sbuf *tn = sbuf_new_auto(); ++ ++ sbuf_printf(tn, "CPU %d", cpu); ++ sbuf_finish(tn); ++ track_uuid = PerfettoTeNamedTrackUuid(sbuf_data(tn), cpu, ++ PerfettoTeGlobalTrackUuid()); ++ perfetto_protos_TrackEvent_set_name(&te, sbuf_data(tn), sbuf_len(tn)); ++ //perfetto_protos_TrackEvent_set_name_iid(&te, cpu); ++ sbuf_delete(tn); ++ ++ perfetto_protos_TrackEvent_set_track_uuid(&te, track_uuid); ++ perfetto_protos_TrackEvent_set_timestamp_absolute_us(&te, data->dtpda_cpu/1000); ++ ++ struct sbuf *cat = sbuf_new_auto(); ++ sbuf_printf(cat, "%s:%s", pd->dtpd_provider, pd->dtpd_func); ++ sbuf_finish(cat); ++ perfetto_protos_TrackEvent_set_categories(&te, sbuf_data(cat), sbuf_len(cat)); ++ sbuf_delete(cat); ++ ++#define TYPE_UNSPECIFIED 0 ++#define TYPE_SLICE_BEGIN 1 ++#define TYPE_SLICE_END 2 ++#define TYPE_INSTANT 3 ++#define TYPE_COUNTER 4 ++ ++ if(strcmp(pd->dtpd_provider, "kinst") == 0) { ++ dctx->kinst_events++; ++ perfetto_protos_TrackEvent_set_type(&te, TYPE_INSTANT); ++ } else if(strcmp(pd->dtpd_provider, "fbt") == 0) { ++ dctx->fbt_events++; ++ if (strcmp(pd->dtpd_name, "entry") == 0) ++ perfetto_protos_TrackEvent_set_type(&te, TYPE_SLICE_BEGIN); ++ else ++ perfetto_protos_TrackEvent_set_type(&te, TYPE_SLICE_END); ++ } ++ perfetto_protos_TracePacket_end_track_event(&root.msg, &te); ++ } ++ ++ PerfettoDsTracerPacketEnd(&ctx, &root); ++// PerfettoDsTracerFlush(&ctx, NULL, NULL); ++ } ++ return (DTRACE_CONSUME_THIS); ++} ++ ++int main(void) { ++ struct PerfettoProducerInitArgs args = PERFETTO_PRODUCER_INIT_ARGS_INIT(); ++ struct PerfettoDsParams ds_params; ++ struct dtrace_context dctx; ++ ++ args.backends = PERFETTO_BACKEND_SYSTEM; ++ PerfettoProducerInit(args); ++ ++ /* Initialise the default parameters */ ++ ds_params = PerfettoDsParamsDefault(); ++ ++ /* Life cycle call backs */ ++ ds_params.on_start_cb = dtrace_on_start_cb; ++ ds_params.on_setup_cb = dtrace_on_setup_cb; ++ ds_params.on_stop_cb = dtrace_on_stop_cb; ++ ds_params.on_destroy_cb = dtrace_on_destroy_cb; ++ ds_params.user_arg = &dctx; ++ ++ PerfettoDsRegister(&custom, "dtrace", ds_params); ++ ++ /* ++ * XXX: TODO We should check priviliges here before trying to create dtrace ++ * probs later when we are setup and configured ++ */ ++ ++ /* Sit in a loop */ ++ for (;;) { ++ /* ++ * If we aren't running sleep for 250 mS, that puts some extra ++ * latency on start, but will be fine for testing. ++ */ ++ if (!dctx.setupandrunning) { ++ usleep(250 * 1000); ++ continue; ++ } ++ ++ /* ++ * dtrace work loop ++ */ ++ switch (dtrace_work(dctx.g_dtp, stdout, NULL, process_record, &dctx)) { ++ case DTRACE_WORKSTATUS_DONE: ++ //done = 1; ++ break; ++ case DTRACE_WORKSTATUS_OKAY: ++ break; ++ default: ++ if (dtrace_errno(dctx.g_dtp) != EINTR) ++ dfail(dctx.g_dtp, "processing aborted"); ++ } ++ ++ dtrace_sleep(dctx.g_dtp); ++ } ++} diff --git a/devel/perfetto/files/patch-examples_dtrace_examples_dtrace__probes.cfg b/devel/perfetto/files/patch-examples_dtrace_examples_dtrace__probes.cfg new file mode 100644 index 000000000000..91cee4601d0c --- /dev/null +++ b/devel/perfetto/files/patch-examples_dtrace_examples_dtrace__probes.cfg @@ -0,0 +1,21 @@ +--- examples/dtrace/examples/dtrace_probes.cfg.orig 2026-07-09 15:43:01 UTC ++++ examples/dtrace/examples/dtrace_probes.cfg +@@ -0,0 +1,18 @@ ++buffers: { ++ size_kb: 8960 ++ fill_policy: DISCARD ++} ++ ++data_sources: { ++ config { ++ name: "dtrace" ++ dtrace_config { ++ dtrace_fbt_probe: "udp_send" ++# dtrace_fbt_probe: "tcp_output" ++# dtrace_kinst_probe: "amd64_syscall:" ++ dtrace_kinst_probe: "udp_input:" ++ } ++ } ++} ++ ++duration_ms: 1000000 diff --git a/devel/perfetto/files/patch-examples_freebsd_BUILD.gn b/devel/perfetto/files/patch-examples_freebsd_BUILD.gn new file mode 100644 index 000000000000..ba71c63199a8 --- /dev/null +++ b/devel/perfetto/files/patch-examples_freebsd_BUILD.gn @@ -0,0 +1,52 @@ +--- examples/freebsd/BUILD.gn.orig 2026-07-09 15:43:01 UTC ++++ examples/freebsd/BUILD.gn +@@ -0,0 +1,49 @@ ++# Copyright (C) 2022 The Android Open Source Project ++# ++# Licensed under the Apache License, Version 2.0 (the "License"); ++# you may not use this file except in compliance with the License. ++# You may obtain a copy of the License at ++# ++# http://www.apache.org/licenses/LICENSE-2.0 ++# ++# Unless required by applicable law or agreed to in writing, software ++# distributed under the License is distributed on an "AS IS" BASIS, ++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++# See the License for the specific language governing permissions and ++# limitations under the License. ++ ++# On windows C11 (required for atomics) is not really supported, so we can't ++# really build a C executable. ++assert(!is_win) ++ ++executable("freebsd_power") { ++ testonly = true ++ deps = [ ++ "../../gn:default_deps", ++ "../../src/shared_lib:libperfetto_c", ++ ] ++ libs = [ ++ "sbuf", ++ ] ++ sources = [ ++ "power.c", ++ ] ++} ++ ++executable("freebsd_sys") { ++ testonly = true ++ deps = [ ++ "../../gn:default_deps", ++ "../../src/shared_lib:libperfetto_c", ++ ] ++ libs = [ ++ "geom", ++ "devstat", ++ ] ++ sources = [ ++ "freebsd_sys_stats.c", ++ "freebsd_sys_stats_cpu.c", ++ "freebsd_sys_stats_disk.c", ++ "freebsd_sys_stats_intr.c", ++ ] ++} diff --git a/devel/perfetto/files/patch-examples_freebsd_examples_power.cfg b/devel/perfetto/files/patch-examples_freebsd_examples_power.cfg new file mode 100644 index 000000000000..6e49a9ef89f2 --- /dev/null +++ b/devel/perfetto/files/patch-examples_freebsd_examples_power.cfg @@ -0,0 +1,15 @@ +--- examples/freebsd/examples/power.cfg.orig 2026-07-09 15:43:01 UTC ++++ examples/freebsd/examples/power.cfg +@@ -0,0 +1,12 @@ ++buffers: { ++ size_kb: 8960 ++ fill_policy: DISCARD ++} ++ ++data_sources: { ++ config { ++ name: "freebsd.power" ++ } ++} ++ ++duration_ms: 10000 diff --git a/devel/perfetto/files/patch-examples_freebsd_freebsd__sys__stats.c b/devel/perfetto/files/patch-examples_freebsd_freebsd__sys__stats.c new file mode 100644 index 000000000000..b636ec709254 --- /dev/null +++ b/devel/perfetto/files/patch-examples_freebsd_freebsd__sys__stats.c @@ -0,0 +1,99 @@ +--- examples/freebsd/freebsd_sys_stats.c.orig 2026-07-09 15:43:01 UTC ++++ examples/freebsd/freebsd_sys_stats.c +@@ -0,0 +1,96 @@ ++/* ++ * Copyright (C) 2022 The Android Open Source Project ++ * ++ * Licensed under the Apache License, Version 2.0 (the "License"); ++ * you may not use this file except in compliance with the License. ++ * You may obtain a copy of the License at ++ * ++ * http://www.apache.org/licenses/LICENSE-2.0 ++ * ++ * Unless required by applicable law or agreed to in writing, software ++ * distributed under the License is distributed on an "AS IS" BASIS, ++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ * See the License for the specific language governing permissions and ++ * limitations under the License. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "perfetto/public/data_source.h" ++#include "perfetto/public/producer.h" ++#include "perfetto/public/protos/trace/sys_stats/sys_stats.pzc.h" ++#include "perfetto/public/protos/trace/trace_packet.pzc.h" ++ ++extern void setup_intrcnt_data(void); ++extern void populate_intrcnt_data(struct perfetto_protos_SysStats* sys_stat); ++ ++extern void setup_disk_data(void); ++extern void populate_disk_data(struct perfetto_protos_SysStats* sys_stat); ++ ++extern void setup_cpu_data(void); ++extern void populate_cpu_data(struct perfetto_protos_SysStats* sys_stat); ++extern void populate_cpu_freq_data(struct perfetto_protos_SysStats* sys_stat); ++extern void populate_cpu_freq_idle(struct perfetto_protos_SysStats* sys_stat); ++ ++static struct PerfettoDs custom = PERFETTO_DS_INIT(); ++ ++static uint64_t get_current_time_ns(void) { ++ struct timespec ts; ++ ++ (void)clock_gettime(CLOCK_BOOTTIME, &ts); ++ ++ return (((uint64_t)ts.tv_sec * 1000000000ULL) + ts.tv_nsec); ++} ++ ++int main(void) { ++ struct PerfettoProducerInitArgs args = PERFETTO_PRODUCER_INIT_ARGS_INIT(); ++ args.backends = PERFETTO_BACKEND_SYSTEM; ++ PerfettoProducerInit(args); ++ ++ /* GEOM for disk stats */ ++ setup_disk_data(); ++ ++ /* intr setup */ ++ setup_intrcnt_data(); ++ ++ /* cpu setup */ ++ setup_cpu_data(); ++ ++ PerfettoDsRegister(&custom, "freebsd.sys_stats", PerfettoDsParamsDefault()); ++ ++ for (;;) { ++ PERFETTO_DS_TRACE(custom, ctx) { ++ struct PerfettoDsRootTracePacket root; ++ PerfettoDsTracerPacketBegin(&ctx, &root); ++ ++ perfetto_protos_TracePacket_set_timestamp(&root.msg, ++ get_current_time_ns()); ++ { ++ struct perfetto_protos_SysStats sys_stats; ++ ++ perfetto_protos_TracePacket_begin_sys_stats(&root.msg, &sys_stats); ++ ++ populate_disk_data(&sys_stats); ++ populate_intrcnt_data(&sys_stats); ++ populate_cpu_data(&sys_stats); ++ populate_cpu_freq_data(&sys_stats); ++ populate_cpu_freq_idle(&sys_stats); ++ ++ perfetto_protos_TracePacket_end_sys_stats(&root.msg, &sys_stats); ++ } ++ PerfettoDsTracerPacketEnd(&ctx, &root); ++ } ++ // 100ms sleep ++ usleep(100 * 1000); ++ // usleep(250 * 1000); ++ // sleep(1); ++ } ++} diff --git a/devel/perfetto/files/patch-examples_freebsd_freebsd__sys__stats__cpu.c b/devel/perfetto/files/patch-examples_freebsd_freebsd__sys__stats__cpu.c new file mode 100644 index 000000000000..0c6b3e2b9869 --- /dev/null +++ b/devel/perfetto/files/patch-examples_freebsd_freebsd__sys__stats__cpu.c @@ -0,0 +1,309 @@ +--- examples/freebsd/freebsd_sys_stats_cpu.c.orig 2026-07-09 15:43:01 UTC ++++ examples/freebsd/freebsd_sys_stats_cpu.c +@@ -0,0 +1,306 @@ ++/* ++ * Copyright (C) 2022 The Android Open Source Project ++ * ++ * Licensed under the Apache License, Version 2.0 (the "License"); ++ * you may not use this file except in compliance with the License. ++ * You may obtain a copy of the License at ++ * ++ * http://www.apache.org/licenses/LICENSE-2.0 ++ * ++ * Unless required by applicable law or agreed to in writing, software ++ * distributed under the License is distributed on an "AS IS" BASIS, ++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ * See the License for the specific language governing permissions and ++ * limitations under the License. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "perfetto/public/data_source.h" ++#include "perfetto/public/producer.h" ++#include "perfetto/public/protos/trace/sys_stats/sys_stats.pzc.h" ++#include "perfetto/public/protos/trace/trace_packet.pzc.h" ++ ++/* ++ * TODO: ++ * ++ * Don't keep reading the list of per-CPU C states each time; ++ * read them once during setup and store them globally! ++ * ++ * TODO: we only support 64 CPUs here (u_long mask)! ++ */ ++ ++struct { ++ int maxcpu; ++ long* times; ++ long* last_cp_times; ++ int times_size; ++ u_long mask; ++ int maxid; ++ bool have_prev; /* TODO: need to clear this between runs */ ++} cpu_info; ++ ++void setup_cpu_data(void) { ++ size_t size; ++ int ret; ++ int empty, i, j; ++ ++ /* Figure out the maximum CPU id */ ++ size = sizeof(int); ++ ret = sysctlbyname("kern.smp.maxcpus", &cpu_info.maxcpu, &size, NULL, 0); ++ if (ret != 0) { ++ printf("%s: kern.smp.maxcpus failed (%d)\n", __func__, ret); ++ return; ++ } ++ ++ printf("maxcpu: %d\n", cpu_info.maxcpu); ++ ++ /* Allocate cpu time array for current and previous cpu times */ ++ cpu_info.times_size = sizeof(long) * cpu_info.maxcpu * CPUSTATES; ++ cpu_info.times = calloc(1, cpu_info.times_size); ++ cpu_info.last_cp_times = calloc(1, cpu_info.times_size); ++ ++ /* Fetch a round of CPU stats, likely to figure out what's up */ ++ size = cpu_info.times_size; ++ ret = sysctlbyname("kern.cp_times", cpu_info.times, &size, NULL, 0); ++ if (ret != 0) { ++ printf("%s: kern.cp_times failed (%d)\n", __func__, errno); ++ return; ++ } ++ ++ /* figure out the maximum id */ ++ cpu_info.maxid = (size / CPUSTATES / sizeof(long)) - 1; ++ ++ for (i = 0; i <= cpu_info.maxid; i++) { ++ empty = 1; ++ for (j = 0; empty && j < CPUSTATES; j++) { ++ if (cpu_info.times[i * CPUSTATES + j] != 0) ++ empty = 0; ++ } ++ if (!empty) ++ cpu_info.mask |= (1ul << i); ++ } ++} ++ ++static unsigned long fetch_cp_time_delta(int cpu, int which) { ++ unsigned long delta; ++ ++ delta = cpu_info.times[cpu * CPUSTATES + which] - ++ cpu_info.last_cp_times[cpu * CPUSTATES + which]; ++ ++ delta = (delta * 1000000000ULL) / CLOCKS_PER_SEC; ++ ++ return (delta); ++} ++ ++void populate_cpu_data(struct perfetto_protos_SysStats* sys_stat) { ++ (void)sys_stat; ++ size_t size; ++ int ret, i; ++ ++ size = cpu_info.times_size; ++ ret = sysctlbyname("kern.cp_times", cpu_info.times, &size, NULL, 0); ++ if (ret != 0) { ++ printf("%s: kern.cp_times failed (%d)\n", __func__, errno); ++ return; ++ } ++ ++ if (cpu_info.have_prev == false) ++ goto skip; ++ ++ for (i = 0; i <= cpu_info.maxid; i++) { ++ struct perfetto_protos_SysStats_CpuTimes cpu_cnt; ++ unsigned long delta; ++ ++ if ((cpu_info.mask & (1ul << i)) == 0) ++ continue; ++ ++ perfetto_protos_SysStats_begin_cpu_stat(sys_stat, &cpu_cnt); ++ ++ /* cpu_id */ ++ perfetto_protos_SysStats_CpuTimes_set_cpu_id(&cpu_cnt, i); ++ ++#if 0 ++ printf("cpu %d: idle=%ld, user=%ld, nice=%ld, system=%ld, intr=%ld\n", ++ i, ++ fetch_cp_time_delta(i, CP_IDLE), ++ fetch_cp_time_delta(i, CP_USER), ++ fetch_cp_time_delta(i, CP_NICE), ++ fetch_cp_time_delta(i, CP_SYS), ++ fetch_cp_time_delta(i, CP_INTR)); ++#endif ++ ++ /* user_ns */ ++ delta = fetch_cp_time_delta(i, CP_USER); ++ perfetto_protos_SysStats_CpuTimes_set_user_ns(&cpu_cnt, delta); ++ ++ /* user_nice_ns */ ++ delta = fetch_cp_time_delta(i, CP_NICE); ++ perfetto_protos_SysStats_CpuTimes_set_user_nice_ns(&cpu_cnt, delta); ++ ++ /* system_mode_ns */ ++ delta = fetch_cp_time_delta(i, CP_SYS); ++ perfetto_protos_SysStats_CpuTimes_set_system_mode_ns(&cpu_cnt, delta); ++ ++ /* idle_ns */ ++ delta = fetch_cp_time_delta(i, CP_IDLE); ++ perfetto_protos_SysStats_CpuTimes_set_idle_ns(&cpu_cnt, delta); ++ ++ /* io_wait_ns */ ++ ++ /* irq_ns */ ++ delta = fetch_cp_time_delta(i, CP_INTR); ++ perfetto_protos_SysStats_CpuTimes_set_irq_ns(&cpu_cnt, delta); ++ ++ /* softirq_ns */ ++ /* steal_ns */ ++ ++ perfetto_protos_SysStats_end_cpu_stat(sys_stat, &cpu_cnt); ++ } ++ ++skip: ++ memcpy(cpu_info.last_cp_times, cpu_info.times, cpu_info.times_size); ++ cpu_info.have_prev = true; ++} ++ ++void populate_cpu_freq_data(struct perfetto_protos_SysStats* sys_stat) { ++ char cpu_string[1024]; ++ size_t size; ++ uint32_t freq; ++ int ret, i; ++ ++ for (i = 0; i <= cpu_info.maxid; i++) { ++ struct perfetto_protos_SysStats_DevfreqValue devfreq; ++ (void)devfreq; ++ ++ snprintf(cpu_string, sizeof(cpu_string), "dev.cpu.%d.freq", i); ++ ++ size = sizeof(freq); ++ ret = sysctlbyname(cpu_string, &freq, &size, NULL, 0); ++ ++ /* This array isn't key/value; we have to populate them */ ++ if (ret != 0) ++ freq = 0; ++ ++ /* ++ * TODO: doing this for the CPU frequency works, but it ++ * doesn't get special treatment in the perfetto UI. ++ * However it's worth doing it for OTHER devices! ++ */ ++#if 0 ++ perfetto_protos_SysStats_begin_devfreq(sys_stat, &devfreq); ++ ++ /* XXX TODO: what should the format of this be? */ ++ snprintf(cpu_string, sizeof(cpu_string), "cpu%d", i); ++ ++ perfetto_protos_SysStats_DevfreqValue_set_key(&devfreq, ++ cpu_string, strlen(cpu_string)); ++ ++ /* Note: freebsd returns MHz, perfetto wants KHz */ ++ perfetto_protos_SysStats_DevfreqValue_set_value(&devfreq, ++ ((uint64_t) freq) * 1000); ++ ++ perfetto_protos_SysStats_end_devfreq(sys_stat, &devfreq); ++#else ++ // There's no key, just add for each CPU ++ perfetto_protos_SysStats_set_cpufreq_khz(sys_stat, freq * 1000); ++#endif ++ } ++} ++ ++/* ++ * Note: this requires some kernel work to support, stay tuned. ++ */ ++void populate_cpu_freq_idle(struct perfetto_protos_SysStats* sys_stat) { ++ char cpu_string[128]; ++ char result[128]; ++ char *c, *r, *cc, *rr; ++ /* XXX hard-coded limit of 8 ACPI states here! */ ++ char cpu_methods[8][64]; ++ size_t size; ++ int ret, i, j; ++ ++ (void)sys_stat; ++ ++ for (i = 0; i <= cpu_info.maxid; i++) { ++ struct perfetto_protos_SysStats_CpuIdleState cpuidle; ++ ++ snprintf(cpu_string, sizeof(cpu_string), "dev.cpu.%d.cx_method", i); ++ ++ /* Fetch the list of sleep state methods */ ++ size = sizeof(result); ++ ret = sysctlbyname(cpu_string, &result, &size, NULL, 0); ++ if (ret != 0) ++ continue; ++ ++ /* Iterate over, space separated */ ++ r = result; ++ j = 0; ++ while ((c = strsep(&r, " ")) != NULL && j < 8) { ++ /* Strip out everything after / */ ++ rr = c; ++ cc = strsep(&rr, "/"); ++ // printf("cpu %d label %s\n", i, cc); ++ snprintf(cpu_methods[j], 64, "%s", cc); ++ j++; ++ } ++ ++ /* Ok, fetch the counters */ ++ ++ snprintf(cpu_string, sizeof(cpu_string), "dev.cpu.%d.cx_duration_counters", ++ i); ++ ++ /* Fetch the list of sleep state methods */ ++ size = sizeof(result); ++ ret = sysctlbyname(cpu_string, &result, &size, NULL, 0); ++ if (ret != 0) ++ continue; ++ ++ perfetto_protos_SysStats_begin_cpuidle_state(sys_stat, &cpuidle); ++ perfetto_protos_SysStats_CpuIdleState_set_cpu_id(&cpuidle, i); ++ ++ /* Iterate over, space separated */ ++ r = result; ++ j = 0; ++ while ((c = strsep(&r, " ")) != NULL && j < 8) { ++ uintmax_t val = 0; ++ ++ val = strtoull(c, NULL, 0); ++ ++ /* Note: this API sucks for error handling */ ++ if (val == ULLONG_MAX) ++ continue; ++ struct perfetto_protos_SysStats_CpuIdleStateEntry cpuentry; ++ perfetto_protos_SysStats_CpuIdleState_begin_cpuidle_state_entry( ++ &cpuidle, &cpuentry); ++ ++ /* Strip out everything after / */ ++ rr = c; ++ cc = strsep(&rr, "/"); ++ // printf("cpu %d %s:%ju \n", i, cpu_methods[j], ++ // val); ++ ++ perfetto_protos_SysStats_CpuIdleStateEntry_set_state( ++ &cpuentry, cpu_methods[j], strlen(cpu_methods[j])); ++ perfetto_protos_SysStats_CpuIdleStateEntry_set_duration_us(&cpuentry, ++ (uint64_t)val); ++ perfetto_protos_SysStats_CpuIdleState_end_cpuidle_state_entry(&cpuidle, ++ &cpuentry); ++ ++ j++; ++ } ++ ++ perfetto_protos_SysStats_end_cpuidle_state(sys_stat, &cpuidle); ++ } ++} diff --git a/devel/perfetto/files/patch-examples_freebsd_freebsd__sys__stats__disk.c b/devel/perfetto/files/patch-examples_freebsd_freebsd__sys__stats__disk.c new file mode 100644 index 000000000000..c885051e7229 --- /dev/null +++ b/devel/perfetto/files/patch-examples_freebsd_freebsd__sys__stats__disk.c @@ -0,0 +1,155 @@ +--- examples/freebsd/freebsd_sys_stats_disk.c.orig 2026-07-09 15:43:01 UTC ++++ examples/freebsd/freebsd_sys_stats_disk.c +@@ -0,0 +1,152 @@ ++/* ++ * Copyright (C) 2022 The Android Open Source Project ++ * ++ * Licensed under the Apache License, Version 2.0 (the "License"); ++ * you may not use this file except in compliance with the License. ++ * You may obtain a copy of the License at ++ * ++ * http://www.apache.org/licenses/LICENSE-2.0 ++ * ++ * Unless required by applicable law or agreed to in writing, software ++ * distributed under the License is distributed on an "AS IS" BASIS, ++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ * See the License for the specific language governing permissions and ++ * limitations under the License. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "perfetto/public/data_source.h" ++#include "perfetto/public/producer.h" ++#include "perfetto/public/protos/trace/sys_stats/sys_stats.pzc.h" ++#include "perfetto/public/protos/trace/trace_packet.pzc.h" ++ ++/* ++ * TODO! ++ * ++ * The trace analyser assumes a linux-ism that /proc/diskstats always ++ * uses 512 byte sector sizes - so for non-512 byte platforms we need ++ * to re-normalise them to 512 sector counts before we push it up. ++ * ++ * Are "discards" in linux procstat the same as _FREE in devstat? ++ * ++ * Maybe see if we can add an optional field to the perfetto trace ++ * record for transfer amount in bytes? So we can populate that, ++ * and then teach the trace processor to believe that from us ++ * rather than synthesise it. ++ * ++ * The synthesized read/write values look a bit trash; please do ++ * a deeper dive into the correctness of these values and ++ * that they match what the linux provider is getting from ++ * /proc/diskstats . ++ */ ++ ++struct { ++ struct gmesh gmp; ++ struct devstat *gsp, *gsq; ++ void *sp, *sq; ++ struct timespec tp, tq; ++ float dt; ++} geom_info; ++ ++void populate_disk_data(struct perfetto_protos_SysStats* sys_stat) { ++ struct gident* gid; ++ char devname[4096]; ++ uint64_t q_len; ++ uint64_t tr_rx, tr_wr, by_rx, by_wr; ++ long double du_rx, du_wr; ++ ++ geom_info.sp = geom_stats_snapshot_get(); ++ if (geom_info.sp == NULL) /* XXX error */ ++ return; ++ geom_stats_snapshot_timestamp(geom_info.sp, &geom_info.tp); ++ ++ geom_info.dt = geom_info.tp.tv_sec - geom_info.tq.tv_sec; ++ geom_info.dt += (geom_info.tp.tv_nsec - geom_info.tq.tv_nsec) * 1e-9; ++ geom_info.tq = geom_info.tp; ++ ++ geom_stats_snapshot_reset(geom_info.sp); ++ geom_stats_snapshot_reset(geom_info.sq); ++ ++ for (;;) { ++ struct perfetto_protos_SysStats_DiskStat disk_stats; ++ ++ geom_info.gsp = geom_stats_snapshot_next(geom_info.sp); ++ geom_info.gsq = geom_stats_snapshot_next(geom_info.sq); ++ ++ if (geom_info.gsp == NULL || geom_info.gsq == NULL) { ++ break; ++ } ++ if (geom_info.gsp->id == NULL) { ++ continue; ++ } ++ ++ /* gstat will delete/reload the tree; here for now just skip */ ++ gid = geom_lookupid(&geom_info.gmp, geom_info.gsp->id); ++ if (gid == NULL) { ++ continue; ++ } ++ ++ /* only consumers */ ++ if (gid->lg_what != ISPROVIDER) { ++ continue; ++ } ++ /* only physical devices for now */ ++ /* note: this EXPECTS it's a provider! */ ++ if (((struct gprovider*)(gid->lg_ptr))->lg_geom->lg_rank != 1) { ++ continue; ++ } ++ /* populate the name - again expects its a provider */ ++ snprintf(devname, sizeof(devname), "%s", ++ ((struct gprovider*)(gid->lg_ptr))->lg_name); ++ ++ /* calculate statistics over the interval */ ++ devstat_compute_statistics( ++ geom_info.gsp, NULL, geom_info.dt, DSM_QUEUE_LENGTH, &q_len, ++ DSM_TOTAL_BLOCKS_READ, &tr_rx, DSM_TOTAL_DURATION_READ, &du_rx, ++ DSM_TOTAL_BYTES_READ, &by_rx, DSM_TOTAL_BLOCKS_WRITE, &tr_wr, ++ DSM_TOTAL_DURATION_WRITE, &du_wr, DSM_TOTAL_BYTES_WRITE, &by_wr, ++ DSM_NONE); ++ ++ // printf("disk: %s, sect read/write %lu/%lu, bytes read/write %lu/%lu, dur ++ // %Lf/%Lf\n", devname, tr_rx, tr_wr, by_rx, by_wr, du_rx, du_wr); ++ ++ /* Populate a disk stat entry */ ++ perfetto_protos_SysStats_begin_disk_stat(sys_stat, &disk_stats); ++ ++ perfetto_protos_SysStats_DiskStat_set_device_name(&disk_stats, devname, ++ strlen(devname)); ++ perfetto_protos_SysStats_DiskStat_set_read_sectors(&disk_stats, tr_rx); ++ perfetto_protos_SysStats_DiskStat_set_read_time_ms( ++ &disk_stats, (uint64_t)(du_rx * 1000)); ++ perfetto_protos_SysStats_DiskStat_set_write_sectors(&disk_stats, tr_wr); ++ perfetto_protos_SysStats_DiskStat_set_write_time_ms( ++ &disk_stats, (uint64_t)(du_wr * 1000)); ++ /* todo: discard_sectors */ ++ /* todo: discard_time_ms */ ++ /* todo: flush_count */ ++ /* todo: flush_time_ms */ ++ ++ perfetto_protos_SysStats_end_disk_stat(sys_stat, &disk_stats); ++ ++ *geom_info.gsq = *geom_info.gsp; ++ geom_stats_snapshot_free(geom_info.sp); ++ } ++} ++ ++void setup_disk_data(void) { ++ /* GEOM for disk stats */ ++ geom_gettree(&geom_info.gmp); ++ geom_stats_open(); ++ /* Get initial disk snapshot */ ++ geom_info.sq = geom_stats_snapshot_get(); ++ geom_stats_snapshot_timestamp(geom_info.sq, &geom_info.tq); ++} diff --git a/devel/perfetto/files/patch-examples_freebsd_freebsd__sys__stats__intr.c b/devel/perfetto/files/patch-examples_freebsd_freebsd__sys__stats__intr.c new file mode 100644 index 000000000000..4619acd82e76 --- /dev/null +++ b/devel/perfetto/files/patch-examples_freebsd_freebsd__sys__stats__intr.c @@ -0,0 +1,83 @@ +--- examples/freebsd/freebsd_sys_stats_intr.c.orig 2026-07-09 15:43:01 UTC ++++ examples/freebsd/freebsd_sys_stats_intr.c +@@ -0,0 +1,80 @@ ++/* ++ * Copyright (C) 2022 The Android Open Source Project ++ * ++ * Licensed under the Apache License, Version 2.0 (the "License"); ++ * you may not use this file except in compliance with the License. ++ * You may obtain a copy of the License at ++ * ++ * http://www.apache.org/licenses/LICENSE-2.0 ++ * ++ * Unless required by applicable law or agreed to in writing, software ++ * distributed under the License is distributed on an "AS IS" BASIS, ++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ * See the License for the specific language governing permissions and ++ * limitations under the License. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "perfetto/public/data_source.h" ++#include "perfetto/public/producer.h" ++#include "perfetto/public/protos/trace/sys_stats/sys_stats.pzc.h" ++#include "perfetto/public/protos/trace/trace_packet.pzc.h" ++ ++struct { ++ uint32_t intrcnt; ++ long* intrcnts_cur; ++ long* intrcnts_prev; ++} intr_info; ++ ++void setup_intrcnt_data(void) { ++ intr_info.intrcnts_cur = calloc(sizeof(long), 131072); ++ intr_info.intrcnts_prev = calloc(sizeof(long), 131072); ++ intr_info.intrcnt = 131072; ++} ++ ++void populate_intrcnt_data(struct perfetto_protos_SysStats* sys_stat) { ++ size_t intrcntlen; ++ int ret, i; ++ ++ intrcntlen = intr_info.intrcnt * sizeof(long); ++ memset(intr_info.intrcnts_cur, 0, intrcntlen); ++ ++ ret = ++ sysctlbyname("hw.intrcnt", intr_info.intrcnts_cur, &intrcntlen, NULL, 0); ++ if (ret != 0) { ++ printf("%s: sysctl failed; %d (%d)\n", __func__, ret, errno); ++ return; ++ } ++ ++ for (i = 0; i < 1024; i++) { ++ long delta; ++ ++ struct perfetto_protos_SysStats_InterruptCount intr_cnt; ++ /* Skip empty interrupt slots, we want cur and prev */ ++ if (intr_info.intrcnts_cur[i] == 0) ++ continue; ++ if (intr_info.intrcnts_prev[i] == 0) ++ continue; ++ ++ delta = intr_info.intrcnts_cur[i] - intr_info.intrcnts_prev[i]; ++ ++ // printf("irq %i: %llu\n", i, (unsigned long long) delta); ++ ++ perfetto_protos_SysStats_begin_num_irq(sys_stat, &intr_cnt); ++ perfetto_protos_SysStats_InterruptCount_set_irq(&intr_cnt, i); ++ perfetto_protos_SysStats_InterruptCount_set_count(&intr_cnt, delta); ++ perfetto_protos_SysStats_end_num_irq(sys_stat, &intr_cnt); ++ } ++ ++ memcpy(intr_info.intrcnts_prev, intr_info.intrcnts_cur, ++ intr_info.intrcnt * sizeof(long)); ++} diff --git a/devel/perfetto/files/patch-examples_freebsd_power.c b/devel/perfetto/files/patch-examples_freebsd_power.c new file mode 100644 index 000000000000..8667ce5d5e2a --- /dev/null +++ b/devel/perfetto/files/patch-examples_freebsd_power.c @@ -0,0 +1,497 @@ +--- examples/freebsd/power.c.orig 2026-07-09 15:43:01 UTC ++++ examples/freebsd/power.c +@@ -0,0 +1,494 @@ ++/* ++ * Copyright (C) 2026 The FreeBSD Foundation ++ * ++ * Portions of this file were written by Tom Jones (thj@freebsd.org) under ++ * sponsorship from The FreeBSD Foundation. ++ * ++ * Licensed under the Apache License, Version 2.0 (the "License"); ++ * you may not use this file except in compliance with the License. ++ * You may obtain a copy of the License at ++ * ++ * http://www.apache.org/licenses/LICENSE-2.0 ++ * ++ * Unless required by applicable law or agreed to in writing, software ++ * distributed under the License is distributed on an "AS IS" BASIS, ++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ * See the License for the specific language governing permissions and ++ * limitations under the License. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include "/usr/include/dev/acpica/acpiio.h" ++ ++#include "perfetto/public/data_source.h" ++#include "perfetto/public/producer.h" ++#include "perfetto/public/pb_decoder.h" ++#include "perfetto/public/protos/trace/power/battery_counters.pzc.h" ++#include "perfetto/public/protos/trace/power/power_rails.pzc.h" ++ ++#include "perfetto/public/track_event.h" ++ ++#define BATTERY 1 ++#define RAPL 1 ++ ++#define ACPIDEV "/dev/acpi" ++/* should be a acpi define, but doesn't appear to be */ ++#define UNKNOWN_CAP 0xffffffff ++#define UNKNOWN_VOLTAGE 0xffffffff ++ ++static struct PerfettoDs custom = PERFETTO_DS_INIT(); ++ ++struct power_context { ++ bool running; ++ uint32_t flags; ++#define PWR_FLAG_ACPIBATTERY 1 ++#define PWR_FLAG_RAPL 2 ++ int acpifd; ++ union acpi_battery_ioctl_arg acpibi; ++ ++ int pp0_mib[4]; ++ size_t pp0_mib_len; ++ uint64_t pp0; ++ ++ int pp1_mib[4]; ++ size_t pp1_mib_len; ++ uint64_t pp1; ++ ++ int platform_mib[4]; ++ size_t platform_mib_len; ++ uint64_t platform; ++ ++ int dram_mib[4]; ++ size_t dram_mib_len; ++ uint64_t dram; ++ ++ uint64_t battery_cap; ++ uint64_t battery_charge; ++ uint64_t battery_rate; ++ uint64_t battery_voltage; ++ ++}; ++ ++int take_battery_reading(struct power_context *pwrctx, int); ++void take_rapl_reading(struct power_context *pwrctx); ++ ++static uint64_t get_current_time_ns(void) { ++ struct timespec ts; ++ ++ (void)clock_gettime(CLOCK_BOOTTIME, &ts); ++ ++ return (((uint64_t)ts.tv_sec * 1000000000ULL) + ts.tv_nsec); ++} ++ ++static void * fbsd_power_on_setup_cb(struct PerfettoDsImpl *impl, ++ PerfettoDsInstanceIndex inst_id, void *ds_config, ++ size_t ds_config_size, void* user_arg, struct PerfettoDsOnSetupArgs *args) { ++ ++ (void)impl; ++ (void)inst_id; ++ (void)ds_config; ++ (void)ds_config_size; ++ (void)args; ++ ++ struct power_context *pwrctx = (struct power_context *)user_arg; ++ ++ pwrctx->acpifd = open(ACPIDEV, O_RDONLY); ++ if (pwrctx->acpifd != -1) { ++ pwrctx->acpibi.unit = 0; // TODO get this from the configuration object ++ if (ioctl(pwrctx->acpifd, ACPIIO_BATT_GET_BST, &pwrctx->acpibi) != -1) { ++ if (pwrctx->acpibi.bst.state != ACPI_BATT_STAT_NOT_PRESENT) { ++ pwrctx->flags |= PWR_FLAG_ACPIBATTERY; ++ printf("ACPI Battery: Battery %d voltage %d\n", ++ pwrctx->acpibi.unit, pwrctx->acpibi.bst.volt); ++ } else { ++ printf("ACPI Battery: Battery %d not present\n", ++ pwrctx->acpibi.unit); ++ } ++ } ++ } else { ++ printf("ACPI Battery: failed to open %s\n", ACPIDEV); ++ } ++ ++ /* Fill out the components of the mib. */ ++ pwrctx->pp0_mib_len = 4; ++ if (sysctlnametomib("dev.intel_rapl.0.pp0", pwrctx->pp0_mib, ++ &pwrctx->pp0_mib_len) == -1) { ++ printf("Couldn't read RAPL pp0\n"); ++ goto out; ++ } ++ ++ pwrctx->pp1_mib_len = 4; ++ if (sysctlnametomib("dev.intel_rapl.0.pp1", pwrctx->pp1_mib, ++ &pwrctx->pp1_mib_len) == -1) { ++ printf("Couldn't read RAPL pp1\n"); ++ goto out; ++ } ++ ++ pwrctx->platform_mib_len = 4; ++ if (sysctlnametomib("dev.intel_rapl.0.platform", pwrctx->platform_mib, ++ &pwrctx->platform_mib_len) == -1) { ++ printf("Couldn't read RAPL platform\n"); ++ goto out; ++ } ++ ++ pwrctx->dram_mib_len = 4; ++ if (sysctlnametomib("dev.intel_rapl.0.dram", pwrctx->dram_mib, ++ &pwrctx->dram_mib_len) == -1) { ++ printf("Couldn't read RAPL dram\n"); ++ goto out; ++ } ++ ++ pwrctx->flags |= PWR_FLAG_RAPL; ++out: ++ printf("freebsd power: %s %s\n", ++ pwrctx->flags & PWR_FLAG_ACPIBATTERY ? "PWR_FLAG_ACPIBATTERY" : "", ++ pwrctx->flags & PWR_FLAG_RAPL ? "PWR_FLAG_RAPL" : ""); ++ return (void *)pwrctx;; ++ ++} ++ ++static void fbsd_power_on_start_cb(struct PerfettoDsImpl* impl, ++ PerfettoDsInstanceIndex inst_id, void* user_arg, ++ void* inst_ctx, struct PerfettoDsOnStartArgs* args) { ++ ++ (void)impl; ++ (void)inst_id; ++ (void)user_arg; ++ (void)inst_ctx; ++ (void)args; ++ ++ printf("%s\n", __func__); ++ ++ struct power_context *pwrctx = (struct power_context *)user_arg; ++ pwrctx->running = true; ++} ++ ++static void fbsd_power_on_stop_cb(struct PerfettoDsImpl *impl, ++ PerfettoDsInstanceIndex inst_id, void* user_arg, void* inst_ctx, ++ struct PerfettoDsOnStopArgs* args) { ++ ++ (void)impl; ++ (void)inst_id; ++ (void)user_arg; ++ (void)inst_ctx; ++ (void)args; ++ ++ printf("%s\n", __func__); ++ ++ struct power_context *pwrctx = (struct power_context *)user_arg; ++ pwrctx->running = false; ++} ++ ++static void fbsd_power_on_destroy_cb(struct PerfettoDsImpl *impl, ++ void* user_arg, void* inst_ctx) { ++ ++ (void)impl; ++ (void)user_arg; ++ (void)inst_ctx; ++ ++ printf("%s\n", __func__); ++} ++ ++int ++take_battery_reading(struct power_context *pwrctx, int idx) ++{ ++ ++ if (idx < 0 || idx > 64) ++ printf("invalid battery %d", idx); ++ ++ /* Print battery design information. */ ++ pwrctx->acpibi.unit = idx; ++ if (ioctl(pwrctx->acpifd, ACPIIO_BATT_GET_BATTINFO, &pwrctx->acpibi) == -1) { ++ printf("get battery info (%d) failed", idx); ++ return 0; ++ } ++ ++ pwrctx->battery_cap = pwrctx->acpibi.battinfo.cap; ++ pwrctx->battery_charge = (((pwrctx->acpibi.battinfo.cap * pwrctx->acpibi.bif.lfcap) / 100)*1000); ++ pwrctx->battery_rate = (pwrctx->acpibi.battinfo.rate * 1000); ++ pwrctx->battery_voltage = pwrctx->acpibi.bst.volt * 1000; ++ ++ return 1; ++} ++ ++void ++take_rapl_reading(struct power_context *pwrctx) ++{ ++ uint64_t value;//, reading; ++ size_t len; ++ ++ value = 0; ++ len = sizeof(value); ++ ++ if (sysctl(pwrctx->pp0_mib, pwrctx->pp0_mib_len, &value, &len, NULL, 0) == -1) { ++ perror("ERROR: sysctl pp0"); ++ return; ++ } ++ pwrctx->pp0 = value; ++ ++ if (sysctl(pwrctx->pp1_mib, pwrctx->pp1_mib_len, &value, &len, NULL, 0) == -1) { ++ perror("ERROR: sysctl pp1"); ++ return; ++ } ++ pwrctx->pp1 = value; ++ ++ if (sysctl(pwrctx->platform_mib, pwrctx->platform_mib_len, &value, &len, NULL, 0) == -1) { ++ perror("ERROR: sysctl platform"); ++ return; ++ } ++ pwrctx->platform = value; ++ ++ if (sysctl(pwrctx->dram_mib, pwrctx->dram_mib_len, &value, &len, NULL, 0) == -1) { ++ perror("ERROR: sysctl dram"); ++ return; ++ } ++ pwrctx->dram = value; ++} ++ ++int main(void) { ++ struct PerfettoProducerInitArgs args = PERFETTO_PRODUCER_INIT_ARGS_INIT(); ++ struct PerfettoDsParams ds_params; ++ struct power_context pwrctx; ++ ++ args.backends = PERFETTO_BACKEND_SYSTEM; ++ PerfettoProducerInit(args); ++ ++ /* Initialise the default parameters */ ++ ds_params = PerfettoDsParamsDefault(); ++ ++ /* Life cycle call backs */ ++ ds_params.on_start_cb = fbsd_power_on_start_cb; ++ ds_params.on_setup_cb = fbsd_power_on_setup_cb; ++ ds_params.on_stop_cb = fbsd_power_on_stop_cb; ++ ds_params.on_destroy_cb = fbsd_power_on_destroy_cb; ++ ds_params.user_arg = &pwrctx; ++ ++ PerfettoDsRegister(&custom, "freebsd.power", ds_params); ++ ++#if BATTERY ++ /* Take an inital readings to set up last */ ++ take_battery_reading(&pwrctx, 0 /* TODO: index */); ++#endif ++#if 0 ++ /* ++ * XXX: The documentation for power rails says we should only have to ++ * emit the descriptive elements once at the start of the trace. When ++ * we do this from the SDK we end up getting no valid values in the ++ * resulting dataset. I suspect there is *something* missing here, but ++ * I'm not sure where to look to figure that out right now. ++ */ ++ take_rapl_reading(&pwrctx); ++ ++ PERFETTO_DS_TRACE(custom, ctx) { ++ struct PerfettoDsRootTracePacket root; ++ struct perfetto_protos_PowerRails rail; ++ struct perfetto_protos_PowerRails_RailDescriptor rd; ++ ++ PerfettoDsTracerPacketBegin(&ctx, &root); ++ perfetto_protos_TracePacket_set_timestamp(&root.msg, get_current_time_ns()); ++ ++ perfetto_protos_TracePacket_begin_power_rails(&root.msg, &rail); ++ perfetto_protos_PowerRails_set_session_uuid(&rail, 1024); ++ ++ perfetto_protos_PowerRails_begin_rail_descriptor(&rail, &rd); ++ ++ perfetto_protos_PowerRails_RailDescriptor_set_index(&rd, 0); ++ perfetto_protos_PowerRails_RailDescriptor_set_rail_name(&rd, "rapl_pp0", sizeof("rapl_pp0")); ++ perfetto_protos_PowerRails_RailDescriptor_set_subsys_name(&rd, "rapl", sizeof("rapl")); ++ perfetto_protos_PowerRails_RailDescriptor_set_sampling_rate(&rd, 1000); ++ ++ perfetto_protos_PowerRails_end_rail_descriptor(&rail, &rd); ++ perfetto_protos_TracePacket_end_power_rails(&root.msg, &rail); ++ ++ perfetto_protos_TracePacket_begin_power_rails(&root.msg, &rail); ++ perfetto_protos_PowerRails_set_session_uuid(&rail, 1024); ++ ++ perfetto_protos_PowerRails_begin_rail_descriptor(&rail, &rd); ++ ++ perfetto_protos_PowerRails_RailDescriptor_set_index(&rd, 1); ++ perfetto_protos_PowerRails_RailDescriptor_set_rail_name(&rd, "rapl_platform", sizeof("rapl_platform")); ++ perfetto_protos_PowerRails_RailDescriptor_set_subsys_name(&rd, "rapl", sizeof("rapl")); ++ perfetto_protos_PowerRails_RailDescriptor_set_sampling_rate(&rd, 1000); ++ ++ perfetto_protos_PowerRails_end_rail_descriptor(&rail, &rd); ++ perfetto_protos_TracePacket_end_power_rails(&root.msg, &rail); ++ ++ PerfettoDsTracerPacketEnd(&ctx, &root); ++ } ++#endif ++ /* Sit in a loop */ ++ for (;;) { ++ /* If we haven't started or nothing has configured take a break */ ++ if (!pwrctx.running || pwrctx.flags == 0) { ++ usleep(1000*1000); ++ continue; ++ } else { ++ usleep(1000); // TODO: This should be configured ++ } ++ ++#if RAPL ++ PERFETTO_DS_TRACE(custom, ctx) { ++ struct PerfettoDsRootTracePacket root; ++ struct perfetto_protos_PowerRails rail; ++ struct perfetto_protos_PowerRails_EnergyData e; ++ struct perfetto_protos_PowerRails_RailDescriptor rd; ++ ++ if (pwrctx.flags & PWR_FLAG_RAPL) { ++ take_rapl_reading(&pwrctx); ++ ++ /* pp0 */ ++ PerfettoDsTracerPacketBegin(&ctx, &root); ++ perfetto_protos_TracePacket_set_timestamp(&root.msg, get_current_time_ns()); ++ ++ perfetto_protos_TracePacket_begin_power_rails(&root.msg, &rail); ++ perfetto_protos_PowerRails_set_session_uuid(&rail, 1024); ++ ++ perfetto_protos_PowerRails_begin_rail_descriptor(&rail, &rd); ++ perfetto_protos_PowerRails_RailDescriptor_set_index(&rd, 0); ++ perfetto_protos_PowerRails_RailDescriptor_set_rail_name(&rd, ++ "pp0", sizeof("pp0")); ++ perfetto_protos_PowerRails_RailDescriptor_set_subsys_name(&rd, ++ "rapl", sizeof("rapl")); ++ perfetto_protos_PowerRails_RailDescriptor_set_sampling_rate(&rd, 1000); ++ perfetto_protos_PowerRails_end_rail_descriptor(&rail, &rd); ++ ++ perfetto_protos_PowerRails_begin_energy_data(&rail, &e); ++ perfetto_protos_PowerRails_EnergyData_set_index(&e, 0); ++ perfetto_protos_PowerRails_EnergyData_set_timestamp_ms(&e, ++ get_current_time_ns()/1000000); ++ perfetto_protos_PowerRails_EnergyData_set_energy(&e, pwrctx.pp0); ++ ++ perfetto_protos_TracePacket_end_power_rails(&root.msg, &rail); ++ PerfettoDsTracerPacketEnd(&ctx, &root); ++ ++ /* pp1 */ ++ PerfettoDsTracerPacketBegin(&ctx, &root); ++ perfetto_protos_TracePacket_set_timestamp(&root.msg, get_current_time_ns()); ++ ++ perfetto_protos_TracePacket_begin_power_rails(&root.msg, &rail); ++ perfetto_protos_PowerRails_set_session_uuid(&rail, 1024); ++ ++ perfetto_protos_PowerRails_begin_rail_descriptor(&rail, &rd); ++ perfetto_protos_PowerRails_RailDescriptor_set_index(&rd, 1); ++ perfetto_protos_PowerRails_RailDescriptor_set_rail_name(&rd, ++ "pp1", sizeof("pp1")); ++ perfetto_protos_PowerRails_RailDescriptor_set_subsys_name(&rd, ++ "rapl", sizeof("rapl")); ++ perfetto_protos_PowerRails_RailDescriptor_set_sampling_rate(&rd, 1000); ++ perfetto_protos_PowerRails_end_rail_descriptor(&rail, &rd); ++ ++ perfetto_protos_PowerRails_begin_energy_data(&rail, &e); ++ perfetto_protos_PowerRails_EnergyData_set_index(&e, 1); ++ perfetto_protos_PowerRails_EnergyData_set_timestamp_ms(&e, ++ get_current_time_ns()/1000000); ++ perfetto_protos_PowerRails_EnergyData_set_energy(&e, pwrctx.platform); ++ perfetto_protos_PowerRails_end_energy_data(&rail, &e); ++ ++ perfetto_protos_TracePacket_end_power_rails(&root.msg, &rail); ++ PerfettoDsTracerPacketEnd(&ctx, &root); ++ ++ /* platform */ ++ PerfettoDsTracerPacketBegin(&ctx, &root); ++ perfetto_protos_TracePacket_set_timestamp(&root.msg, get_current_time_ns()); ++ ++ perfetto_protos_TracePacket_begin_power_rails(&root.msg, &rail); ++ perfetto_protos_PowerRails_set_session_uuid(&rail, 1024); ++ ++ perfetto_protos_PowerRails_begin_rail_descriptor(&rail, &rd); ++ perfetto_protos_PowerRails_RailDescriptor_set_index(&rd, 1); ++ perfetto_protos_PowerRails_RailDescriptor_set_rail_name(&rd, ++ "platform", sizeof("platform")); ++ perfetto_protos_PowerRails_RailDescriptor_set_subsys_name(&rd, ++ "rapl", sizeof("rapl")); ++ perfetto_protos_PowerRails_RailDescriptor_set_sampling_rate(&rd, 1000); ++ perfetto_protos_PowerRails_end_rail_descriptor(&rail, &rd); ++ ++ perfetto_protos_PowerRails_begin_energy_data(&rail, &e); ++ perfetto_protos_PowerRails_EnergyData_set_index(&e, 1); ++ perfetto_protos_PowerRails_EnergyData_set_timestamp_ms(&e, ++ get_current_time_ns()/1000000); ++ perfetto_protos_PowerRails_EnergyData_set_energy(&e, pwrctx.platform); ++ perfetto_protos_PowerRails_end_energy_data(&rail, &e); ++ ++ perfetto_protos_TracePacket_end_power_rails(&root.msg, &rail); ++ PerfettoDsTracerPacketEnd(&ctx, &root); ++ ++ /* dram */ ++ PerfettoDsTracerPacketBegin(&ctx, &root); ++ perfetto_protos_TracePacket_set_timestamp(&root.msg, get_current_time_ns()); ++ ++ perfetto_protos_TracePacket_begin_power_rails(&root.msg, &rail); ++ perfetto_protos_PowerRails_set_session_uuid(&rail, 1024); ++ ++ perfetto_protos_PowerRails_begin_rail_descriptor(&rail, &rd); ++ perfetto_protos_PowerRails_RailDescriptor_set_index(&rd, 1); ++ perfetto_protos_PowerRails_RailDescriptor_set_rail_name(&rd, ++ "dram", sizeof("dram")); ++ perfetto_protos_PowerRails_RailDescriptor_set_subsys_name(&rd, ++ "rapl", sizeof("rapl")); ++ perfetto_protos_PowerRails_RailDescriptor_set_sampling_rate(&rd, 1000); ++ perfetto_protos_PowerRails_end_rail_descriptor(&rail, &rd); ++ ++ perfetto_protos_PowerRails_begin_energy_data(&rail, &e); ++ perfetto_protos_PowerRails_EnergyData_set_index(&e, 1); ++ perfetto_protos_PowerRails_EnergyData_set_timestamp_ms(&e, ++ get_current_time_ns()/1000000); ++ perfetto_protos_PowerRails_EnergyData_set_energy(&e, pwrctx.platform); ++ perfetto_protos_PowerRails_end_energy_data(&rail, &e); ++ ++ perfetto_protos_TracePacket_end_power_rails(&root.msg, &rail); ++ PerfettoDsTracerPacketEnd(&ctx, &root); ++ } ++ } ++#endif ++#if BATTERY ++ PERFETTO_DS_TRACE(custom, ctx) { ++ struct PerfettoDsRootTracePacket root; ++ struct perfetto_protos_BatteryCounters bat; ++ ++ if (pwrctx.flags & PWR_FLAG_ACPIBATTERY) { ++ take_battery_reading(&pwrctx, 0 /* TODO: battery index */); ++ ++ PerfettoDsTracerPacketBegin(&ctx, &root); ++ perfetto_protos_TracePacket_set_timestamp(&root.msg, get_current_time_ns()); ++ ++ perfetto_protos_TracePacket_begin_battery(&root.msg, &bat); ++ ++ perfetto_protos_BatteryCounters_set_name(&bat, "acpi0", sizeof("acpi0")); ++ ++ if (pwrctx.acpibi.bif.units == ACPI_BIF_UNITS_MW) { ++ /* TODO */ ++ } else { ++ perfetto_protos_BatteryCounters_set_charge_counter_uah(&bat, ++ pwrctx.battery_charge); /* power in ui */ ++ perfetto_protos_BatteryCounters_set_current_avg_ua(&bat, ++ pwrctx.battery_rate); ++ perfetto_protos_BatteryCounters_set_current_ua(&bat, ++ pwrctx.battery_rate); ++ ++ } ++ ++ perfetto_protos_BatteryCounters_set_capacity_percent(&bat, pwrctx.battery_cap); ++ perfetto_protos_BatteryCounters_set_voltage_uv(&bat, pwrctx.battery_voltage); ++ perfetto_protos_TracePacket_end_battery(&root.msg, &bat); ++ ++ PerfettoDsTracerPacketEnd(&ctx, &root); ++ } ++ } ++#endif ++ ++ } ++} diff --git a/devel/perfetto/files/patch-include_perfetto_public_BUILD.gn b/devel/perfetto/files/patch-include_perfetto_public_BUILD.gn new file mode 100644 index 000000000000..e05d2e6b16c5 --- /dev/null +++ b/devel/perfetto/files/patch-include_perfetto_public_BUILD.gn @@ -0,0 +1,10 @@ +--- include/perfetto/public/BUILD.gn.orig 2026-07-07 14:09:45 UTC ++++ include/perfetto/public/BUILD.gn +@@ -36,6 +36,7 @@ source_set("public") { + "thread_utils.h", + "tracing_session.h", + "track_event.h", ++ "cpu_info.h", + ] + public_deps = [ + ":base", diff --git a/devel/perfetto/files/patch-protos_perfetto_config_BUILD.gn b/devel/perfetto/files/patch-protos_perfetto_config_BUILD.gn new file mode 100644 index 000000000000..e6ab703c9192 --- /dev/null +++ b/devel/perfetto/files/patch-protos_perfetto_config_BUILD.gn @@ -0,0 +1,10 @@ +--- protos/perfetto/config/BUILD.gn.orig 2026-07-07 14:09:45 UTC ++++ protos/perfetto/config/BUILD.gn +@@ -24,6 +24,7 @@ perfetto_proto_library("@TYPE@") { + "../common:semantic_type_@TYPE@", + "android:@TYPE@", + "ftrace:@TYPE@", ++ "dtrace:@TYPE@", + "gpu:@TYPE@", + "inode_file:@TYPE@", + "interceptors:@TYPE@", diff --git a/devel/perfetto/files/patch-protos_perfetto_config_data__source__config.proto b/devel/perfetto/files/patch-protos_perfetto_config_data__source__config.proto new file mode 100644 index 000000000000..23d044d9bbe8 --- /dev/null +++ b/devel/perfetto/files/patch-protos_perfetto_config_data__source__config.proto @@ -0,0 +1,29 @@ +--- protos/perfetto/config/data_source_config.proto.orig 2026-07-07 14:09:45 UTC ++++ protos/perfetto/config/data_source_config.proto +@@ -44,6 +44,7 @@ import "protos/perfetto/config/ftrace/frozen_ftrace_co + import "protos/perfetto/config/chrome/system_metrics.proto"; + import "protos/perfetto/config/ftrace/ftrace_config.proto"; + import "protos/perfetto/config/ftrace/frozen_ftrace_config.proto"; ++import "protos/perfetto/config/dtrace/dtrace_config.proto"; + import "protos/perfetto/config/gpu/gpu_counter_config.proto"; + import "protos/perfetto/config/gpu/vulkan_memory_config.proto"; + import "protos/perfetto/config/gpu/gpu_renderstages_config.proto"; +@@ -66,7 +67,7 @@ import "protos/perfetto/config/qnx/qnx_config.proto"; + import "protos/perfetto/config/qnx/qnx_config.proto"; + + // The configuration that is passed to each data source when starting tracing. +-// Next id: 143 ++// Next id: 144 + message DataSourceConfig { + enum SessionInitiator { + SESSION_INITIATOR_UNSPECIFIED = 0; +@@ -312,6 +313,9 @@ message DataSourceConfig { + + // Data source name: android.display.video + optional DisplayVideoConfig display_video_config = 142 [lazy = true]; ++ ++ // Data source name: dtrace ++ optional DtraceConfig dtrace_config = 143 [lazy = true]; + + // Data source name: qnx.kernel + optional QnxConfig qnx_config = 150 [lazy = true]; diff --git a/devel/perfetto/files/patch-protos_perfetto_config_dtrace_BUILD.gn b/devel/perfetto/files/patch-protos_perfetto_config_dtrace_BUILD.gn new file mode 100644 index 000000000000..89a671f9a235 --- /dev/null +++ b/devel/perfetto/files/patch-protos_perfetto_config_dtrace_BUILD.gn @@ -0,0 +1,25 @@ +--- protos/perfetto/config/dtrace/BUILD.gn.orig 2026-07-09 15:43:01 UTC ++++ protos/perfetto/config/dtrace/BUILD.gn +@@ -0,0 +1,22 @@ ++# Copyright (C) 2019 The Android Open Source Project ++# ++# Licensed under the Apache License, Version 2.0 (the "License"); ++# you may not use this file except in compliance with the License. ++# You may obtain a copy of the License at ++# ++# http://www.apache.org/licenses/LICENSE-2.0 ++# ++# Unless required by applicable law or agreed to in writing, software ++# distributed under the License is distributed on an "AS IS" BASIS, ++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++# See the License for the specific language governing permissions and ++# limitations under the License. ++ ++import("../../../../gn/perfetto.gni") ++import("../../../../gn/proto_library.gni") ++ ++perfetto_proto_library("@TYPE@") { ++ sources = [ ++ "dtrace_config.proto", ++ ] ++} diff --git a/devel/perfetto/files/patch-protos_perfetto_config_dtrace_dtrace__config.proto b/devel/perfetto/files/patch-protos_perfetto_config_dtrace_dtrace__config.proto new file mode 100644 index 000000000000..0709b8093398 --- /dev/null +++ b/devel/perfetto/files/patch-protos_perfetto_config_dtrace_dtrace__config.proto @@ -0,0 +1,34 @@ +--- protos/perfetto/config/dtrace/dtrace_config.proto.orig 2026-07-09 15:43:01 UTC ++++ protos/perfetto/config/dtrace/dtrace_config.proto +@@ -0,0 +1,31 @@ ++/* ++ * Copyright (C) 2026 The FreeBSD Foundation ++ * ++ * Portions of this file were written by Tom Jones under sponsorship from the ++ * FreeBSD Foundation. ++ * ++ * Licensed under the Apache License, Version 2.0 (the "License"); ++ * you may not use this file except in compliance with the License. ++ * You may obtain a copy of the License at ++ * ++ * http://www.apache.org/licenses/LICENSE-2.0 ++ * ++ * Unless required by applicable law or agreed to in writing, software ++ * distributed under the License is distributed on an "AS IS" BASIS, ++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ * See the License for the specific language governing permissions and ++ * limitations under the License. ++ */ ++ ++syntax = "proto2"; ++package perfetto.protos; ++ ++// Next id: 3 ++message DtraceConfig { ++ ++ // Dtrace fbt entry probes to record ++ repeated string dtrace_fbt_probe = 1; ++ ++ // Dtrace fbt return probes to record ++ repeated string dtrace_kinst_probe = 2; ++} diff --git a/devel/perfetto/files/patch-src_tracebox_tracebox.cc b/devel/perfetto/files/patch-src_tracebox_tracebox.cc new file mode 100644 index 000000000000..3184d561b4da --- /dev/null +++ b/devel/perfetto/files/patch-src_tracebox_tracebox.cc @@ -0,0 +1,60 @@ +--- src/tracebox/tracebox.cc.orig 2026-07-07 14:09:45 UTC ++++ src/tracebox/tracebox.cc +@@ -57,6 +57,37 @@ const Applet g_applets[]{ + {"websocket_bridge", WebsocketBridgeMain}, + }; + ++#if PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) ++const char *freebsd_applets[] { ++ "dtrace_probe", ++}; ++ ++bool ++start_freebsd_applets(int tracedpid) ++{ ++ std::string self_path = base::GetCurExecutablePath(); ++ ++ for (size_t i = 0; i < nitems(freebsd_applets); i++) { ++ printf("starting freebsd applet %s\n", freebsd_applets[i]); ++ ++ base::Subprocess app({self_path, freebsd_applets[i]}); ++ ++ // Put app in the same process group as traced. Same ++ // reason (CTRL+C) but it's not worth creating a new group. ++ app.args.posix_proc_group_id = tracedpid; ++ ++ base::Pipe app_sync_pipe = base::Pipe::Create(); ++ int app_fd = *app_sync_pipe.wr; ++ base::SetEnv("TRACED_PROBES_NOTIFY_FD", std::to_string(app_fd)); ++ app.args.preserve_fds.emplace_back(app_fd); ++ ++ app.Start(); ++ app_sync_pipe.wr.reset(); ++ } ++ return true; ++} ++#endif ++ + void PrintUsage() { + printf(R"(Welcome to Perfetto tracing! + +@@ -196,6 +227,7 @@ int TraceboxMain(int argc, char** argv) { + PERFETTO_FATAL("The tracing service failed unexpectedly. Check the logs"); + #endif + ++#if !PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) + base::Subprocess traced_probes( + {self_path, "traced_probes", "--reset-ftrace"}); + // Put traced_probes in the same process group as traced. Same reason (CTRL+C) +@@ -246,6 +278,11 @@ int TraceboxMain(int argc, char** argv) { + PERFETTO_ELOG( + "Unsupported: linux.perf data source support (traced_perf) " + "compiled-out."); ++#endif ++#else ++ ++ if (!start_freebsd_applets(traced.pid())) ++ return 0; + #endif + + perfetto_cmd.ConnectToServiceRunAndMaybeNotify(); diff --git a/devel/perfetto/pkg-plist b/devel/perfetto/pkg-plist index a64eb7cd6447..ab95a7f05320 100644 --- a/devel/perfetto/pkg-plist +++ b/devel/perfetto/pkg-plist @@ -1,10 +1,38 @@ -bin/dtrace_probe -bin/freebsd_power -bin/freebsd_sys bin/perfetto +bin/perfetto_dtrace_probe +bin/perfetto_freebsd_power +bin/perfetto_freebsd_sys bin/tracebox bin/traceconv bin/traced bin/traced_relay lib/libperfetto.a lib/libperfetto_c.so +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/README.md +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/dtrace/BUILD.gn +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/dtrace/dtrace_probe.c +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/dtrace/examples/dtrace_probes.cfg +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/freebsd/BUILD.gn +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/freebsd/examples/power.cfg +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/freebsd/freebsd_sys_stats.c +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/freebsd/freebsd_sys_stats_cpu.c +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/freebsd/freebsd_sys_stats_disk.c +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/freebsd/freebsd_sys_stats_intr.c +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/freebsd/power.c +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/sdk/BUILD.gn +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/sdk/CMakeLists.txt +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/sdk/README.md +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/sdk/example.cc +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/sdk/example.png +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/sdk/example_console.cc +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/sdk/example_custom_data_source.cc +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/sdk/example_startup_trace.cc +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/sdk/example_system_wide.cc +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/sdk/example_system_wide.png +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/sdk/system_wide_trace_cfg.pbtxt +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/sdk/trace_categories.cc +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/sdk/trace_categories.h +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/shared_lib/BUILD.gn +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/shared_lib/README.md +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/shared_lib/example_shlib_data_source.c +%%PORTEXAMPLES%%%%EXAMPLESDIR%%/shared_lib/example_shlib_track_event.c