| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/developer/debug/zxdb/client/pretty_stack_manager.h" |
| |
| #include <algorithm> |
| |
| #include "src/developer/debug/zxdb/client/stack.h" |
| |
| namespace zxdb { |
| |
| void PrettyStackManager::SetMatchers(std::vector<StackGlob> matchers) { |
| matchers_ = std::move(matchers); |
| |
| // The matchers must always go from the largest to the smallest. |
| std::sort(matchers_.begin(), matchers_.end(), [](const auto& left, const auto& right) { |
| return left.frames.size() > right.frames.size(); |
| }); |
| } |
| |
| // TODO(bug 43549) this should be loaded from a configuration file somehow associated with the |
| // user's build instead of being hardcoded. |
| void PrettyStackManager::LoadDefaultMatchers() { |
| std::vector<StackGlob> matchers; |
| |
| // C async loop waiting. |
| StackGlob c_async_loop( |
| "Waiting for event in async_loop_run()", |
| {PrettyFrameGlob::Wildcard(1, 1), // syscalls-<platform>.S |
| PrettyFrameGlob::Func("_zx_port_wait"), PrettyFrameGlob::Func("async_loop_run_once"), |
| PrettyFrameGlob::Func("async_loop_run")}); |
| |
| // C++ async loop waiting (just adds a call to the C version). |
| StackGlob cpp_async_loop("Waiting for event in async::Loop::Run()", c_async_loop.frames); |
| cpp_async_loop.frames.push_back(PrettyFrameGlob::Func("async::Loop::Run")); |
| |
| matchers.push_back(std::move(cpp_async_loop)); |
| matchers.push_back(std::move(c_async_loop)); |
| |
| // Typical background thread startup. |
| StackGlob pthread_startup("pthread startup", {PrettyFrameGlob::Func("start_pthread"), |
| PrettyFrameGlob::Func("thread_trampoline")}); |
| |
| // std::thread startup (wraps pthread startup). This has a crazy function name the matcher can't |
| // support so accept anything from std::thread being called from pthread startup. |
| StackGlob std_thread_startup("std::thread startup", {PrettyFrameGlob::File("thread")}); |
| std_thread_startup.frames.insert(std_thread_startup.frames.end(), pthread_startup.frames.begin(), |
| pthread_startup.frames.end()); |
| |
| matchers.push_back(std::move(pthread_startup)); |
| matchers.push_back(std::move(std_thread_startup)); |
| |
| // Async loop thread startup. Don't count "async_loop_run" because we count that as part of runing |
| // and task dispatch (dispatch will be the same with a loop and without). |
| matchers.push_back(StackGlob("async_loop thread startup", |
| {PrettyFrameGlob::FuncFile("async_loop_run_thread", "loop.c"), |
| PrettyFrameGlob::FuncFile("start_c11", "pthread_create.c"), |
| PrettyFrameGlob::Func("thread_trampoline")})); |
| |
| // Async loop task dispatch. |
| matchers.push_back(StackGlob("Dispatching task from async loop", |
| {PrettyFrameGlob::Func("async_loop_dispatch_task"), |
| PrettyFrameGlob::Func("async_loop_dispatch_tasks"), |
| PrettyFrameGlob::Func("async_loop_run")})); |
| |
| // fpromise::promise and fit::function occur a lot and generate extremely long and useless names. |
| // Matching useful sequences is difficult. But just replacing individual stack entries with |
| // a simple string eliminates ~3 lines of template goop and ~3 lines of unnecessary function |
| // parameters. This makes backtraces much easier to read. |
| matchers.push_back( |
| StackGlob("fpromise::promise code", {PrettyFrameGlob::File("fit/promise_internal.h")})); |
| matchers.push_back(StackGlob("fpromise::promise code", {PrettyFrameGlob::File("fit/promise.h")})); |
| matchers.push_back(StackGlob("fit::function code", {PrettyFrameGlob::File("fit/function.h")})); |
| matchers.push_back( |
| StackGlob("fit::function code", {PrettyFrameGlob::File("fit/function_internal.h")})); |
| |
| // Rust async loop waiting. |
| StackGlob rust_async_loop( |
| "Waiting for event in Executor::run_singlethreaded()", |
| {PrettyFrameGlob::Wildcard(1, 1), // syscalls file (name depends on platform). |
| PrettyFrameGlob::Func("_zx_port_wait"), |
| PrettyFrameGlob::Func("fuchsia_zircon::port::Port::wait"), |
| PrettyFrameGlob::Wildcard(2, 2), // Lambdas |
| PrettyFrameGlob::Func("std::thread::local::LocalKey<*>::try_with<*>"), |
| PrettyFrameGlob::Func("std::thread::local::LocalKey<*>::with<*>"), |
| PrettyFrameGlob::Func("fuchsia_async::runtime::fuchsia::executor::with_local_timer_heap<*>"), |
| PrettyFrameGlob::Func( |
| "fuchsia_async::runtime::fuchsia::executor::Executor::run_singlethreaded<*>")}); |
| matchers.push_back(std::move(rust_async_loop)); |
| |
| // C startup code (although it is only one frame, it hides several lines of complex useless |
| // parameters in the "backtrace" view). The function name depends on the platform, so just match |
| // the file name. |
| PrettyFrameGlob libc_start_main = PrettyFrameGlob::File("__libc_start_main.c"); |
| matchers.push_back(StackGlob("libc startup", {libc_start_main})); |
| |
| // Rust startup code, debug. |
| matchers.push_back(StackGlob( |
| "Rust startup", |
| {PrettyFrameGlob::File("src/libstd/rt.rs"), PrettyFrameGlob::File("src/libstd/rt.rs"), |
| PrettyFrameGlob::Func("std::panicking::try::do_call<*>"), |
| PrettyFrameGlob::Func("std::panicking::try<*>"), |
| PrettyFrameGlob::Func("std::panic::catch_unwind<*>"), |
| PrettyFrameGlob::Func("std::rt::lang_start_internal"), |
| PrettyFrameGlob::Func("std::rt::lang_start<*>"), |
| PrettyFrameGlob::Func("main"), // C main function |
| libc_start_main})); |
| |
| // Rust startup code, optimized. |
| matchers.push_back( |
| StackGlob("Rust startup", {PrettyFrameGlob::File("src/libstd/rt.rs"), |
| PrettyFrameGlob::Func("std::rt::lang_start_internal"), |
| PrettyFrameGlob::Func("std::rt::lang_start<*>"), |
| PrettyFrameGlob::Func("main"), // C main function |
| libc_start_main})); |
| |
| SetMatchers(std::move(matchers)); |
| } |
| |
| PrettyStackManager::Match PrettyStackManager::GetMatchAt(const Stack& stack, |
| size_t frame_index) const { |
| for (const StackGlob& matcher : matchers_) { |
| if (size_t match_count = StackGlobMatchesAt(matcher, stack, frame_index)) |
| return Match(match_count, matcher.description); |
| } |
| |
| return Match(); |
| } |
| |
| std::vector<PrettyStackManager::FrameEntry> PrettyStackManager::ProcessStack( |
| const Stack& stack) const { |
| std::vector<FrameEntry> result; |
| for (size_t stack_i = 0; stack_i < stack.size(); /* nothing */) { |
| FrameEntry& entry = result.emplace_back(); |
| entry.begin_index = stack_i; |
| entry.match = GetMatchAt(stack, stack_i); |
| |
| if (entry.match) { |
| for (size_t entry_i = 0; entry_i < entry.match.match_count; entry_i++) |
| entry.frames.push_back(stack[stack_i + entry_i]); |
| stack_i += entry.match.match_count; |
| } else { |
| // No match, append single stack entry. |
| entry.frames.push_back(stack[stack_i]); |
| stack_i++; |
| } |
| } |
| |
| return result; |
| } |
| |
| // static |
| size_t PrettyStackManager::StackGlobMatchesAt(const StackGlob& stack_glob, const Stack& stack, |
| size_t frame_start_index) { |
| if (frame_start_index + stack_glob.frames.size() > stack.size()) |
| return 0; // Not enough room for all frame globs. |
| |
| size_t glob_index = 0; |
| size_t stack_index = frame_start_index; |
| |
| // Number of wildcard positions left to possibly (not not necessarily) skip |
| size_t wildcard_skip = 0; |
| |
| while (glob_index < stack_glob.frames.size() && stack_index < stack.size()) { |
| const PrettyFrameGlob& frame_glob = stack_glob.frames[glob_index]; |
| const Frame* frame = stack[stack_index]; |
| |
| if (frame_glob.is_wildcard()) { |
| FX_DCHECK(!wildcard_skip); |
| wildcard_skip = frame_glob.max_matches() - frame_glob.min_matches(); |
| // The min_matches will be eaten at the bottom of the loop. |
| } else if (frame_glob.Matches(frame)) { |
| wildcard_skip = 0; |
| } else if (wildcard_skip == 0) { |
| return 0; |
| } else { |
| wildcard_skip--; |
| stack_index++; |
| continue; |
| } |
| glob_index++; |
| stack_index += frame_glob.min_matches(); |
| } |
| |
| if (stack_index > stack.size()) |
| return 0; // Wildcard minimum is off the end of the stack. |
| |
| // Matched to the bottom of the stack. |
| return stack_index - frame_start_index; |
| } |
| |
| } // namespace zxdb |