| // 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. | |
| #ifndef SRC_DEVELOPER_DEBUG_ZXDB_CLIENT_PRETTY_STACK_MANAGER_H_ | |
| #define SRC_DEVELOPER_DEBUG_ZXDB_CLIENT_PRETTY_STACK_MANAGER_H_ | |
| #include <vector> | |
| #include "gtest/gtest_prod.h" | |
| #include "src/developer/debug/zxdb/client/pretty_frame_glob.h" | |
| #include "src/lib/fxl/memory/ref_counted.h" | |
| namespace zxdb { | |
| class Stack; | |
| // Matches sequences of frames for pretty-ifying stacks. The patterns are expressed in | |
| // PrettyFrameGlobs which match different parts of the stack frame or a wildcard range. The matched | |
| // frames can then be collapsed and named with a descriptive string. | |
| // | |
| // Wildcard matching matches as few stack entries as possible (unlike many regular expression | |
| // systems). Recursion can make the same sequence of frames appear multiple times in a stack, and | |
| // we always want to hide as few frames as possible. | |
| // | |
| // Wildcard matching does not do backtracking. This means that the first frame after a wildcard | |
| // picks up matching the stack again. If the sequence of matchers after this doesn't match, the | |
| // code won't go search for a possibly different interpretation of the wildcard that does match. | |
| // This behavior is unnecessary given typical stack matching requirements and affects complexity and | |
| // performance. | |
| class PrettyStackManager : public fxl::RefCountedThreadSafe<PrettyStackManager> { | |
| public: | |
| // Construct with fxl::MakeRefCounted(). | |
| struct StackGlob { | |
| StackGlob(std::string d, std::vector<PrettyFrameGlob> f) | |
| : description(std::move(d)), frames(std::move(f)) {} | |
| std::string description; | |
| std::vector<PrettyFrameGlob> frames; | |
| }; | |
| struct Match { | |
| Match() = default; | |
| Match(size_t mc, const std::string& d) : match_count(mc), description(d) {} | |
| // Returns true if this matches any frames. | |
| operator bool() const { return match_count > 0; } | |
| size_t match_count = 0; | |
| std::string description; | |
| }; | |
| // The grouped results of prettifying an entire stack. | |
| struct FrameEntry { | |
| // Index into the stack fo the first frame. | |
| size_t begin_index = 0; | |
| // Match if there was one starting at this location. | |
| Match match; | |
| // The frames corresponding to this item. If there's a match, this will contain the range of | |
| // frames identified by the match_count. If there's no match, this will contain one frame. | |
| std::vector<const Frame*> frames; | |
| }; | |
| void SetMatchers(std::vector<StackGlob> matchers); | |
| // Loads the hardcoded default matchers. | |
| // | |
| // TODO(bug 43549) this should be loaded from a configuration file somehow associated with the | |
| // user's build instead of being hardcoded. This function can then be deleted. | |
| void LoadDefaultMatchers(); | |
| // Returns the best match at the given index. The result will match no frames if there was no | |
| // match at the given index. | |
| Match GetMatchAt(const Stack& stack, size_t frame_index) const; | |
| // Processes an entire stack, returning the grouped results. | |
| std::vector<FrameEntry> ProcessStack(const Stack& stack) const; | |
| private: | |
| FRIEND_REF_COUNTED_THREAD_SAFE(PrettyStackManager); | |
| FRIEND_MAKE_REF_COUNTED(PrettyStackManager); | |
| FRIEND_TEST(PrettyStackManager, StackGlobMatchesAt); | |
| PrettyStackManager() = default; | |
| // Returns the number of frames matched (may be > stack_glob.size() if there were wildcard | |
| // matches). Will return 0 if it's not a match. | |
| static size_t StackGlobMatchesAt(const StackGlob& stack_glob, const Stack& stack, | |
| size_t frame_start_index); | |
| // Sorted in order of decreasing size (longest matchers are first). | |
| std::vector<StackGlob> matchers_; | |
| }; | |
| } // namespace zxdb | |
| #endif // SRC_DEVELOPER_DEBUG_ZXDB_CLIENT_PRETTY_STACK_MANAGER_H_ |