Add Ad Hoc Flamegraph Upload and Visualization#97
Merged
artursarlo merged 2 commits intodynamic_profilingfrom Mar 12, 2026
Merged
Add Ad Hoc Flamegraph Upload and Visualization#97artursarlo merged 2 commits intodynamic_profilingfrom
artursarlo merged 2 commits intodynamic_profilingfrom
Conversation
prashantbytesyntax
approved these changes
Mar 12, 2026
Contributor
prashantbytesyntax
left a comment
There was a problem hiding this comment.
once merged, @marc-queiroz can you help test the feature in dynamic profiling branch
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add Ad Hoc Flamegraph Upload and Visualization
Summary
This PR introduces end-to-end support for ad hoc (on-demand) flamegraph profiles alongside the existing continuous profiling flow. When gprofiler agents run in ad hoc mode and emit a
flamegraph_htmlfield in the processed file payload, the system now:continuousflag in file metadata.products/{service}/stacks/flamegraph/), decoding base64-encoded payloads as needed.AdhocFlamegraphMetadata).Command tracking for heartbeats (
received_command_ids/executed_command_ids) is also wired through the full stack as part of this change.Changes by Component
Database / Migrations
scripts/setup/postgres/migrations/add_dynamic_profiling.up.sqlscripts/setup/postgres/gprofiler_recreate.sqlAdhocFlamegraphMetadatatable with columns:id,service_id,hostname,s3_key,start_time,end_time,perf_events,created_at.AdhocFlamegraphMetadata: onservice_id, onhostname, and a composite on(service_id, start_time, end_time).received_command_ids uuid[] NULLandexecuted_command_ids uuid[] NULLcolumns toHostHeartbeats.scripts/setup/postgres/migrations/add_dynamic_profiling.down.sqlDROP TABLE IF EXISTS AdhocFlamegraphMetadata CASCADEand the corresponding index drops.Go Indexer (
src/gprofiler_indexer/)callstacks.goProfilingTypeAdhoc = "adhoc"andProfilingTypeContinuous = "continuous"constants.RunArgumentsin the file metadata struct withContinuous bool,PerfEvents string, andPerfMode stringfields.FileInfowith anHTMLBlobandFlamegraphHTMLfield to receive the flamegraph HTML payload from the SQS message.FlamegraphHTMLis non-empty, the indexer:metadata.continuous.products/{service}/stacks/flamegraph/{filename}_{type}_flamegraph.htmlin S3 (supports both plain HTML and base64-encoded payloads).StoreAdhocFlamegraphMetadata()to persist the record in PostgreSQL.postgres.go(new file)InitPostgres(connStr string)— opens a*sql.DBconnection usinglib/pq.StoreAdhocFlamegraphMetadata(serviceID, hostname, s3Key, startTime, endTime, perfEvents)— inserts a row intoAdhocFlamegraphMetadata.ClosePostgres()— closes the connection pool.args.goPostgresHost,PostgresPort,PostgresUser,PostgresPassword,PostgresDBCLI flag fields with defaults andGPROFILER_POSTGRES_*environment variable overrides.main.goInitPostgres(connStr)at startup, withdefer ClosePostgres().go.mod/go.sumgithub.com/lib/pq v1.10.9dependency.Backend API — Python (
src/gprofiler/backend/,src/gprofiler-dev/)routers/metrics_routes.pyFlamegraphFilePydantic model (s3_key,hostname,start_time,end_time,perf_events,exists).FlamegraphContentPydantic model (contentstring for HTML).GET /metrics/adhoc_flamegraphsendpoint — queriesAdhocFlamegraphMetadatafor a service/time range, checks S3 existence in parallel, and returns a list ofFlamegraphFileobjects (withexists=Falsefor removed files).GET /metrics/adhoc_flamegraph_contentendpoint — streams the flamegraph HTML from S3 and returns it asFlamegraphContent.received_command_idsandexecuted_command_idsthrough to the DB upsert.models/metrics_models.pyreceived_command_ids: Optional[List[str]]andexecuted_command_ids: Optional[List[str]]toHeartbeatRequest.gprofiler-dev/gprofiler_dev/postgres/db_manager.pyget_adhoc_flamegraphs_metadata(service_id, start_time, end_time, hostname_filters)— queriesAdhocFlamegraphMetadatawith optional hostname filtering via RQL.upsert_host_heartbeatto accept and persistreceived_command_idsandexecuted_command_ids.gprofiler-dev/gprofiler_dev/s3_profile_dal.pycheck_keys_exist(keys: List[str]) -> Dict[str, bool]— checks S3 object existence in parallel usingThreadPoolExecutorwith HEAD requests.list_files_with_prefix(prefix: str) -> List[str]— lists S3 keys under a given prefix.backend/utils/filters_utils.pyget_rql_all_eq_values(rql_filter, field_name)helper for extracting multi-value equality filters from RQL expressions, used for hostname filtering in the adhoc flamegraph query.Frontend (
src/gprofiler/frontend/src/)api/urls.jsGET_ADHOC_FLAMEGRAPHSandGET_ADHOC_FLAMEGRAPH_CONTENTURL constants.utils/consts.jsadhoc: 'adhoc'to thePROFILES_VIEWSenum.components/profiles/views/adhoc/AdhocProfilingView.jsx(new file)/adhoc_flamegraph_content.components/profiles/ProfilesWrapper.jsxshouldShowView = isFgDisplayed || viewMode === PROFILES_VIEWS.adhocto bypass the flamegraph-empty guard when the adhoc view is active.components/profiles/ProfilesViewsWrapper.jsxisAdhocViewflag andshouldDisplayView = isFgDisplayed || isAdhocViewto allow rendering without a loaded flamegraph.components/profiles/ProfilesViews.jsx<AdhocProfilingView />directly whenviewMode === PROFILES_VIEWS.adhoc, before reaching the main flamegraph render path.isFGEmptyside effect to skip navigation away from the adhoc view.components/profiles/header/viewModeSwitch/ViewModeSwitch.jsx[PROFILES_VIEWS.adhoc]: ICONS_NAMES.FlameGraphViewto theVIEW_TO_ICON_NAMEmap so the tab icon renders correctly.components/profiles/header/viewModeSwitch/ViewModeTooltip.jsxadhocentry to theviewModesobject (fixes aTypeError: Cannot destructure property 'label'crash when the adhoc view mode was selected).components/console/ProfilingStatusPage.jsxbuildProfileUrl(view)helper.?view=flamegraph) and "View Adhoc Profile" (→?view=adhoc).Deploy
deploy/docker-compose.ymlGPROFILER_POSTGRES_HOST,GPROFILER_POSTGRES_PORT,GPROFILER_POSTGRES_USER,GPROFILER_POSTGRES_PASSWORD,GPROFILER_POSTGRES_DB) to the indexer service definition.Testing Checklist
flamegraph_htmlset andcontinuous: falseto theadhocpath and stores metadata in PostgreSQL.continuous: trueto the S3 upload-only path (no PostgreSQL insert).GET /metrics/adhoc_flamegraphsreturns the correct list of files withexistsstatus.GET /metrics/adhoc_flamegraph_contentreturns valid HTML for an existing S3 key.TypeErroron the view mode tooltip when switching to the adhoc tab.received_command_idsandexecuted_command_ids.add_dynamic_profiling.up.sqlmigration applies cleanly;down.sqlrolls back without errors.