1 /*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "src/trace_processor/importers/common/legacy_v8_cpu_profile_tracker.h"
18
19 #include <cstdint>
20 #include <optional>
21 #include <utility>
22
23 #include "perfetto/base/status.h"
24 #include "perfetto/ext/base/flat_hash_map.h"
25 #include "perfetto/ext/base/status_or.h"
26 #include "perfetto/ext/base/string_view.h"
27 #include "src/trace_processor/importers/common/mapping_tracker.h"
28 #include "src/trace_processor/importers/common/process_tracker.h"
29 #include "src/trace_processor/importers/common/stack_profile_tracker.h"
30 #include "src/trace_processor/storage/trace_storage.h"
31 #include "src/trace_processor/tables/profiler_tables_py.h"
32 #include "src/trace_processor/types/trace_processor_context.h"
33
34 namespace perfetto::trace_processor {
35
LegacyV8CpuProfileTracker(TraceProcessorContext * context)36 LegacyV8CpuProfileTracker::LegacyV8CpuProfileTracker(
37 TraceProcessorContext* context)
38 : context_(context) {}
39
SetStartTsForSessionAndPid(uint64_t session_id,uint32_t pid,int64_t ts)40 void LegacyV8CpuProfileTracker::SetStartTsForSessionAndPid(uint64_t session_id,
41 uint32_t pid,
42 int64_t ts) {
43 auto [it, inserted] = state_by_session_and_pid_.Insert(
44 std::make_pair(session_id, pid),
45 State{ts, base::FlatHashMap<uint32_t, CallsiteId>(), nullptr});
46 it->ts = ts;
47 if (inserted) {
48 it->mapping = &context_->mapping_tracker->CreateDummyMapping("");
49 }
50 }
51
AddCallsite(uint64_t session_id,uint32_t pid,uint32_t raw_callsite_id,std::optional<uint32_t> parent_raw_callsite_id,base::StringView script_url,base::StringView function_name)52 base::Status LegacyV8CpuProfileTracker::AddCallsite(
53 uint64_t session_id,
54 uint32_t pid,
55 uint32_t raw_callsite_id,
56 std::optional<uint32_t> parent_raw_callsite_id,
57 base::StringView script_url,
58 base::StringView function_name) {
59 auto* state = state_by_session_and_pid_.Find(std::make_pair(session_id, pid));
60 if (!state) {
61 return base::ErrStatus(
62 "v8 profile id does not exist: cannot insert callsite");
63 }
64 FrameId frame_id =
65 state->mapping->InternDummyFrame(function_name, script_url);
66 CallsiteId callsite_id;
67 if (parent_raw_callsite_id) {
68 auto* parent_id = state->callsites.Find(*parent_raw_callsite_id);
69 if (!parent_id) {
70 return base::ErrStatus(
71 "v8 profile parent id does not exist: cannot insert callsite");
72 }
73 auto row =
74 context_->storage->stack_profile_callsite_table().FindById(*parent_id);
75 callsite_id = context_->stack_profile_tracker->InternCallsite(
76 *parent_id, frame_id, row->depth() + 1);
77 } else {
78 callsite_id = context_->stack_profile_tracker->InternCallsite(std::nullopt,
79 frame_id, 0);
80 }
81 if (!state->callsites.Insert(raw_callsite_id, callsite_id).second) {
82 return base::ErrStatus("v8 profile: callsite with id already exists");
83 }
84 return base::OkStatus();
85 }
86
AddDeltaAndGetTs(uint64_t session_id,uint32_t pid,int64_t delta_ts)87 base::StatusOr<int64_t> LegacyV8CpuProfileTracker::AddDeltaAndGetTs(
88 uint64_t session_id,
89 uint32_t pid,
90 int64_t delta_ts) {
91 auto* state = state_by_session_and_pid_.Find(std::make_pair(session_id, pid));
92 if (!state) {
93 return base::ErrStatus(
94 "v8 profile id does not exist: cannot compute timestamp from delta");
95 }
96 state->ts += delta_ts;
97 return state->ts;
98 }
99
AddSample(int64_t ts,uint64_t session_id,uint32_t pid,uint32_t tid,uint32_t raw_callsite_id)100 base::Status LegacyV8CpuProfileTracker::AddSample(int64_t ts,
101 uint64_t session_id,
102 uint32_t pid,
103 uint32_t tid,
104 uint32_t raw_callsite_id) {
105 auto* state = state_by_session_and_pid_.Find(std::make_pair(session_id, pid));
106 if (!state) {
107 return base::ErrStatus("v8 callsite id does not exist: cannot add sample");
108 }
109 auto* id = state->callsites.Find(raw_callsite_id);
110 if (!id) {
111 return base::ErrStatus("v8 callsite id does not exist: cannot add sample");
112 }
113 UniqueTid utid = context_->process_tracker->UpdateThread(tid, pid);
114 auto* samples = context_->storage->mutable_cpu_profile_stack_sample_table();
115 samples->Insert({ts, *id, utid, 0});
116 return base::OkStatus();
117 }
118
119 } // namespace perfetto::trace_processor
120