| /* |
| * Copyright (C) 2020 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 "src/profiling/common/callstack_trie.h" |
| |
| #include <vector> |
| |
| #include "perfetto/ext/base/string_splitter.h" |
| #include "src/profiling/common/interner.h" |
| #include "src/profiling/common/unwind_support.h" |
| |
| namespace perfetto { |
| namespace profiling { |
| |
| GlobalCallstackTrie::Node* GlobalCallstackTrie::GetOrCreateChild( |
| Node* self, |
| const Interned<Frame>& loc) { |
| Node* child = self->GetChild(loc); |
| if (!child) |
| child = self->AddChild(loc, ++next_callstack_id_, self); |
| return child; |
| } |
| |
| std::vector<Interned<Frame>> GlobalCallstackTrie::BuildInverseCallstack( |
| const Node* node) const { |
| std::vector<Interned<Frame>> res; |
| while (node != &root_) { |
| res.emplace_back(node->location_); |
| node = node->parent_; |
| } |
| return res; |
| } |
| |
| GlobalCallstackTrie::Node* GlobalCallstackTrie::CreateCallsite( |
| const std::vector<unwindstack::FrameData>& callstack, |
| const std::vector<std::string>& build_ids) { |
| PERFETTO_CHECK(callstack.size() == build_ids.size()); |
| Node* node = &root_; |
| // libunwindstack gives the frames top-first, but we want to bookkeep and |
| // emit as bottom first. |
| auto callstack_it = callstack.crbegin(); |
| auto build_id_it = build_ids.crbegin(); |
| for (; callstack_it != callstack.crend() && build_id_it != build_ids.crend(); |
| ++callstack_it, ++build_id_it) { |
| const unwindstack::FrameData& loc = *callstack_it; |
| const std::string& build_id = *build_id_it; |
| node = GetOrCreateChild(node, InternCodeLocation(loc, build_id)); |
| } |
| return node; |
| } |
| |
| GlobalCallstackTrie::Node* GlobalCallstackTrie::CreateCallsite( |
| const std::vector<Interned<Frame>>& callstack) { |
| Node* node = &root_; |
| // libunwindstack gives the frames top-first, but we want to bookkeep and |
| // emit as bottom first. |
| for (auto it = callstack.crbegin(); it != callstack.crend(); ++it) { |
| const Interned<Frame>& loc = *it; |
| node = GetOrCreateChild(node, loc); |
| } |
| return node; |
| } |
| |
| void GlobalCallstackTrie::IncrementNode(Node* node) { |
| while (node != nullptr) { |
| node->ref_count_ += 1; |
| node = node->parent_; |
| } |
| } |
| |
| void GlobalCallstackTrie::DecrementNode(Node* node) { |
| PERFETTO_DCHECK(node->ref_count_ >= 1); |
| |
| bool delete_prev = false; |
| Node* prev = nullptr; |
| while (node != nullptr) { |
| if (delete_prev) |
| node->RemoveChild(prev); |
| node->ref_count_ -= 1; |
| delete_prev = node->ref_count_ == 0; |
| prev = node; |
| node = node->parent_; |
| } |
| } |
| |
| Interned<Frame> GlobalCallstackTrie::InternCodeLocation( |
| const unwindstack::FrameData& loc, |
| const std::string& build_id) { |
| Mapping map(string_interner_.Intern(build_id)); |
| map.exact_offset = loc.map_exact_offset; |
| map.start_offset = loc.map_elf_start_offset; |
| map.start = loc.map_start; |
| map.end = loc.map_end; |
| map.load_bias = loc.map_load_bias; |
| base::StringSplitter sp(loc.map_name, '/'); |
| while (sp.Next()) |
| map.path_components.emplace_back(string_interner_.Intern(sp.cur_token())); |
| |
| Frame frame(mapping_interner_.Intern(std::move(map)), |
| string_interner_.Intern(loc.function_name), loc.rel_pc); |
| |
| return frame_interner_.Intern(frame); |
| } |
| |
| Interned<Frame> GlobalCallstackTrie::MakeRootFrame() { |
| Mapping map(string_interner_.Intern("")); |
| |
| Frame frame(mapping_interner_.Intern(std::move(map)), |
| string_interner_.Intern(""), 0); |
| |
| return frame_interner_.Intern(frame); |
| } |
| |
| GlobalCallstackTrie::Node* GlobalCallstackTrie::Node::AddChild( |
| const Interned<Frame>& loc, |
| uint64_t callstack_id, |
| Node* parent) { |
| auto it = children_.emplace(loc, callstack_id, parent); |
| return const_cast<Node*>(&(*it.first)); |
| } |
| void GlobalCallstackTrie::Node::RemoveChild(Node* node) { |
| children_.erase(*node); |
| } |
| |
| GlobalCallstackTrie::Node* GlobalCallstackTrie::Node::GetChild( |
| const Interned<Frame>& loc) { |
| // This will be nicer with C++14 transparent comparators. |
| // Then we will be able to look up by just the key using a sutiable |
| // comparator. |
| // |
| // For now we need to allow to construct Node from the key. |
| Node node(loc); |
| auto it = children_.find(node); |
| if (it == children_.end()) |
| return nullptr; |
| return const_cast<Node*>(&(*it)); |
| } |
| |
| } // namespace profiling |
| } // namespace perfetto |