blob: 1a262937b0c27bf0f774fcee586effc4af599cc7 [file]
/*
* Copyright (C) 2026 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.
*/
#ifndef SRC_TRACE_PROCESSOR_PLUGINS_FLAMECHART_FLAMECHART_H_
#define SRC_TRACE_PROCESSOR_PLUGINS_FLAMECHART_FLAMECHART_H_
#include <cstdint>
#include "perfetto/ext/base/status_or.h"
#include "src/trace_processor/containers/string_pool.h"
#include "src/trace_processor/core/dataframe/dataframe.h"
#include "src/trace_processor/core/tree/tree.h"
#include "src/trace_processor/core/util/span.h"
namespace perfetto::trace_processor::flamechart {
// Computes the flame-chart rectangle set from a stack tree and a series of
// sample points.
//
// The tree encodes the stack structure: `tree.parent` links each node to its
// caller (row indices, kNullParent for roots, parents before children). Each
// point is a (ts[i], leaf_id[i]) pair: a sample timestamp and the tree node
// of the innermost (leaf) frame at that time. |ts| must be non-decreasing and
// the two spans must have equal size. Leaf ids are original node ids (e.g. a
// callsite id), resolved through the id -> row index persisted on the tree by
// core::BuildTree; for trees without an id column the ids are row indices.
// Unresolvable leaves are skipped: open segments are kept as-is and the gap
// does not contribute to sample counts.
//
// The returned dataframe has five columns, in this order:
// 0: ts - segment start timestamp
// 1: dur - segment duration, or -1 if still open at the last point
// 2: depth - stack depth (0 = outermost/root frame)
// 3: id - original node id of the segment's frame (the tree's id
// column), so runs join directly against the tree's source table; row
// index for trees without an id column
// 4: sample_count - number of points in the segment
//
// The output is the maximal-run (prefix-merge) decomposition: for each depth,
// a segment spans the time range over which the same frame was continuously
// present at that depth. Consecutive points sharing a stack prefix extend the
// shared segments; only divergent depths open new segments. This keeps the
// output far below (points x depth): the leaf depth yields about one segment
// per run while shallow depths yield only a handful. Segments still open
// after the last point have duration -1 (incomplete). Zero-duration segments
// from stack transitions at equal timestamps are omitted.
//
// The sweep is a single pass over the points. Each point walks its stack and
// updates counts at every depth, so time is O(sum of sample stack depths).
// Memory is O(maximum stack depth) plus the emitted output.
base::StatusOr<dataframe::Dataframe> Build(const core::Tree& tree,
core::Span<const int64_t> ts,
core::Span<const int64_t> leaf_id,
StringPool* pool);
} // namespace perfetto::trace_processor::flamechart
#endif // SRC_TRACE_PROCESSOR_PLUGINS_FLAMECHART_FLAMECHART_H_