blob: 5ab4433ff2a520c058c3a1ec8570de16da796107 [file] [log] [blame]
// Copyright 2020 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 <lib/syslog/cpp/macros.h>
#include <gtest/gtest.h>
#include "src/developer/debug/zxdb/client/mock_frame.h"
#include "src/developer/debug/zxdb/client/mock_stack_delegate.h"
#include "src/developer/debug/zxdb/client/session.h"
#include "src/developer/debug/zxdb/symbols/function.h"
namespace zxdb {
namespace {
std::vector<std::unique_ptr<Frame>> GetTestFrames() {
std::vector<std::unique_ptr<Frame>> frames;
frames.push_back(std::make_unique<MockFrame>(nullptr, nullptr, 0x1001, 0x2001, "Function1",
FileLine("file1.cc", 23)));
frames.push_back(std::make_unique<MockFrame>(nullptr, nullptr, 0x1002, 0x2002, "Function2",
FileLine("file2.cc", 23)));
frames.push_back(std::make_unique<MockFrame>(nullptr, nullptr, 0x1003, 0x2003, "Function3",
FileLine("file3.cc", 23)));
frames.push_back(std::make_unique<MockFrame>(nullptr, nullptr, 0x1004, 0x2004, "Function4",
FileLine("file4.cc", 23)));
return frames;
}
} // namespace
TEST(PrettyStackManager, StackGlobMatchesAt) {
Session session;
MockStackDelegate delegate(&session);
Stack stack(&delegate);
delegate.set_stack(&stack);
stack.SetFramesForTest(GetTestFrames(), true);
// Single item that's not a match.
PrettyStackManager::StackGlob single_unmatched("", {PrettyFrameGlob::Func("Unmatched")});
EXPECT_EQ(0u, PrettyStackManager::StackGlobMatchesAt(single_unmatched, stack, 0));
EXPECT_EQ(0u, PrettyStackManager::StackGlobMatchesAt(single_unmatched, stack, 1));
EXPECT_EQ(0u, PrettyStackManager::StackGlobMatchesAt(single_unmatched, stack, 2));
// Single function that is a match.
PrettyStackManager::StackGlob single_matched_func("", {PrettyFrameGlob::Func("Function2")});
EXPECT_EQ(1u, PrettyStackManager::StackGlobMatchesAt(single_matched_func, stack, 1));
// Single file that is a match.
PrettyStackManager::StackGlob single_matched_file("", {PrettyFrameGlob::File("file2.cc")});
EXPECT_EQ(1u, PrettyStackManager::StackGlobMatchesAt(single_matched_file, stack, 1));
// Function match followed by file match.
PrettyStackManager::StackGlob double_match(
"", {PrettyFrameGlob::Func("Function2"), PrettyFrameGlob::File("file3.cc")});
EXPECT_EQ(0u, PrettyStackManager::StackGlobMatchesAt(double_match, stack, 0));
EXPECT_EQ(2u, PrettyStackManager::StackGlobMatchesAt(double_match, stack, 1));
// Wildcard that matches one thing at the beginning
PrettyStackManager::StackGlob single_wildcard_beginning(
"", {PrettyFrameGlob::Wildcard(1, 1), PrettyFrameGlob::File("file2.cc")});
EXPECT_EQ(2u, PrettyStackManager::StackGlobMatchesAt(single_wildcard_beginning, stack, 0));
EXPECT_EQ(0u, PrettyStackManager::StackGlobMatchesAt(single_wildcard_beginning, stack, 1));
// Wildcard that matches exactly two things in the middle.
PrettyStackManager::StackGlob double_wildcard_middle(
"", {PrettyFrameGlob::File("file1.cc"), PrettyFrameGlob::Wildcard(2, 2),
PrettyFrameGlob::Func("Function4")});
EXPECT_EQ(4u, PrettyStackManager::StackGlobMatchesAt(double_wildcard_middle, stack, 0));
EXPECT_EQ(0u, PrettyStackManager::StackGlobMatchesAt(double_wildcard_middle, stack, 1));
// Wildcard that doesn't match.
PrettyStackManager::StackGlob wildcard_miss(
"", {PrettyFrameGlob::File("file1.cc"), PrettyFrameGlob::Wildcard(1, 1),
PrettyFrameGlob::Func("Function4")});
EXPECT_EQ(0u, PrettyStackManager::StackGlobMatchesAt(wildcard_miss, stack, 0));
// Wildcard that matches anything at the end. Since the wildcard matches as few items as possible,
// it should match the minimum range when the wildcard is at the end of the glob list (in this
// case 1 frame, giving 2 total frames matched).
PrettyStackManager::StackGlob wildcard_end(
"", {PrettyFrameGlob::File("file2.cc"), PrettyFrameGlob::Wildcard(1, 3)});
EXPECT_EQ(0u, PrettyStackManager::StackGlobMatchesAt(wildcard_end, stack, 0));
EXPECT_EQ(2u, PrettyStackManager::StackGlobMatchesAt(wildcard_end, stack, 1));
// Wildcard runs off the end.
PrettyStackManager::StackGlob wildcard_off_end(
"", {PrettyFrameGlob::File("file2.cc"), PrettyFrameGlob::Wildcard(4, 4)});
EXPECT_EQ(0u, PrettyStackManager::StackGlobMatchesAt(wildcard_off_end, stack, 1));
// Wildcard requires too few items (but would otherwise match).
PrettyStackManager::StackGlob wildcard_too_many(
"", {PrettyFrameGlob::File("file1.cc"), PrettyFrameGlob::Wildcard(1, 1),
PrettyFrameGlob::File("file4.cc")});
EXPECT_EQ(0u, PrettyStackManager::StackGlobMatchesAt(wildcard_too_many, stack, 1));
// This wildcard allows (but doesn't require) running off the end but requires another entry after
// it (that doesn't match in this case) so it won't match.
PrettyStackManager::StackGlob wildcard_mismatch_off_end(
"", {PrettyFrameGlob::File("file1.cc"), PrettyFrameGlob::Wildcard(1, 8),
PrettyFrameGlob::File("NOT_PRESENT.cc")});
EXPECT_EQ(0u, PrettyStackManager::StackGlobMatchesAt(wildcard_mismatch_off_end, stack, 0));
}
TEST(PrettyStackManager, GetMatchAt) {
Session session;
MockStackDelegate delegate(&session);
Stack stack(&delegate);
delegate.set_stack(&stack);
stack.SetFramesForTest(GetTestFrames(), true);
auto manager = fxl::MakeRefCounted<PrettyStackManager>();
// Empty matchers should never match.
EXPECT_FALSE(manager->GetMatchAt(stack, 0));
EXPECT_FALSE(manager->GetMatchAt(stack, 1));
EXPECT_FALSE(manager->GetMatchAt(stack, 2));
EXPECT_FALSE(manager->GetMatchAt(stack, 3));
// Supply two non-overlapping matchers.
std::vector<PrettyStackManager::StackGlob> matchers;
matchers.push_back(
PrettyStackManager::StackGlob("Function1 Matcher", {PrettyFrameGlob::Func("Function1")}));
matchers.push_back(
PrettyStackManager::StackGlob("file2 Matcher", {PrettyFrameGlob::File("file2.cc")}));
manager->SetMatchers(matchers);
PrettyStackManager::Match result = manager->GetMatchAt(stack, 0);
EXPECT_TRUE(result);
EXPECT_EQ(1u, result.match_count);
EXPECT_EQ("Function1 Matcher", result.description);
result = manager->GetMatchAt(stack, 1);
EXPECT_TRUE(result);
EXPECT_EQ(1u, result.match_count);
EXPECT_EQ("file2 Matcher", result.description);
// Now add on top of that a wildcard match that covers more range, it should now be returned in
// both cases since its longer.
matchers.push_back(PrettyStackManager::StackGlob(
"Star Matcher", {PrettyFrameGlob::Wildcard(1, 2), PrettyFrameGlob::File("file3.cc")}));
manager->SetMatchers(matchers);
result = manager->GetMatchAt(stack, 0);
EXPECT_TRUE(result);
EXPECT_EQ(3u, result.match_count);
EXPECT_EQ("Star Matcher", result.description);
result = manager->GetMatchAt(stack, 1);
EXPECT_TRUE(result);
EXPECT_EQ(2u, result.match_count);
EXPECT_EQ("Star Matcher", result.description);
}
TEST(PrettyStackManager, ProcessStack) {
Session session;
MockStackDelegate delegate(&session);
Stack stack(&delegate);
delegate.set_stack(&stack);
stack.SetFramesForTest(GetTestFrames(), true);
auto manager = fxl::MakeRefCounted<PrettyStackManager>();
// These two matchers have the same description, the PrettyStackManager should merge them.
std::vector<PrettyStackManager::StackGlob> matchers;
std::string matcher_name("Function1/2 Matcher");
matchers.push_back(
PrettyStackManager::StackGlob(matcher_name, {PrettyFrameGlob::Func("Function1")}));
matchers.push_back(
PrettyStackManager::StackGlob(matcher_name, {PrettyFrameGlob::Func("Function2")}));
manager->SetMatchers(matchers);
// Should get:
// 0-1: "Function1/2 Matcher"
// 2: Function3
// 3: Function4
std::vector<PrettyStackManager::FrameEntry> entries = manager->ProcessStack(stack);
ASSERT_EQ(3u, entries.size());
// The combined Function1/2 match.
EXPECT_EQ(0u, entries[0].begin_index);
EXPECT_EQ(matcher_name, entries[0].match.description);
EXPECT_EQ(2u, entries[0].match.match_count);
ASSERT_EQ(2u, entries[0].frames.size());
EXPECT_EQ(stack[0], entries[0].frames[0]);
EXPECT_EQ(stack[1], entries[0].frames[1]);
// Function3 (bare).
EXPECT_EQ(2u, entries[1].begin_index);
EXPECT_FALSE(entries[1].match);
ASSERT_EQ(1u, entries[1].frames.size());
EXPECT_EQ(stack[2], entries[1].frames[0]);
// Function4 (bare);
EXPECT_EQ(3u, entries[2].begin_index);
EXPECT_FALSE(entries[2].match);
ASSERT_EQ(1u, entries[2].frames.size());
EXPECT_EQ(stack[3], entries[2].frames[0]);
}
} // namespace zxdb