1 /* Any copyright is dedicated to the Public Domain.
2  * http://creativecommons.org/publicdomain/zero/1.0/
3  */
4 
5 #include "TelemetryFixture.h"
6 #include "TelemetryTestHelpers.h"
7 #include "other/CombinedStacks.h"
8 #include "other/ProcessedStack.h"
9 #include "nsPrintfCString.h"
10 
11 using namespace mozilla::Telemetry;
12 using namespace TelemetryTestHelpers;
13 
TEST_F(TelemetryTestFixture,CombinedStacks)14 TEST_F(TelemetryTestFixture, CombinedStacks) {
15   const size_t kMaxStacksKept = 10;
16   CombinedStacks stacks(kMaxStacksKept);
17 
18   size_t iterations = kMaxStacksKept * 2;
19   for (size_t i = 0; i < iterations; ++i) {
20     ProcessedStack stack;
21     ProcessedStack::Frame frame = {static_cast<uint16_t>(i)};
22     const nsAutoString& name =
23         NS_ConvertUTF8toUTF16(nsPrintfCString("test%zu", i));
24     ProcessedStack::Module module = {name};
25 
26     stack.AddFrame(frame);
27     stack.AddModule(module);
28     stacks.AddStack(stack);
29   }
30 
31   ASSERT_EQ(stacks.GetStackCount(), kMaxStacksKept) << "Wrong number of stacks";
32   ASSERT_EQ(stacks.GetModuleCount(), kMaxStacksKept * 2)
33       << "Wrong number of modules";
34 
35   for (size_t i = 0; i < kMaxStacksKept; ++i) {
36     ProcessedStack::Frame frame = stacks.GetStack(i)[0];
37     ASSERT_EQ(frame.mOffset, kMaxStacksKept + i)
38         << "Frame is not returning expected value";
39 
40     ProcessedStack::Module module = stacks.GetModule(frame.mModIndex);
41     nsPrintfCString moduleName("test%hu", frame.mModIndex);
42     ASSERT_TRUE(module.mName.Equals(NS_ConvertUTF8toUTF16(moduleName)))
43     << "Module should have expected name";
44   }
45 
46   for (size_t i = 0; i < kMaxStacksKept; ++i) {
47     stacks.RemoveStack(kMaxStacksKept - i - 1);
48     ASSERT_EQ(stacks.GetStackCount(), kMaxStacksKept - i - 1)
49         << "Stack should be removed";
50   }
51 }
52 
53 template <int N>
MakeStack(const nsLiteralString (& aModules)[N],const uintptr_t (& aOffsets)[N])54 ProcessedStack MakeStack(const nsLiteralString (&aModules)[N],
55                          const uintptr_t (&aOffsets)[N]) {
56   ProcessedStack stack;
57   for (int i = 0; i < N; ++i) {
58     ProcessedStack::Frame frame = {aOffsets[i]};
59     if (aModules[i].IsEmpty()) {
60       frame.mModIndex = std::numeric_limits<uint16_t>::max();
61     } else {
62       frame.mModIndex = stack.GetNumModules();
63       stack.AddModule(ProcessedStack::Module{aModules[i]});
64     }
65     stack.AddFrame(frame);
66   }
67   return stack;
68 }
69 
TEST(CombinedStacks,Combine)70 TEST(CombinedStacks, Combine)
71 {
72   const nsLiteralString moduleSet1[] = {u"mod1"_ns, u"mod2"_ns, u"base"_ns};
73   const nsLiteralString moduleSet2[] = {u"modX"_ns, u""_ns, u"modZ"_ns,
74                                         u"base"_ns};
75   // [0] 00 mod1+100
76   //     01 mod2+200
77   //     02 base+300
78   // [1] 00 mod1+1000
79   //     01 mod2+2000
80   //     02 base+3000
81   // [2] 00 modX+100
82   //     01 <no module>+200
83   //     02 modZ+300
84   //     03 base+400
85   // [3] 00 modX+1000
86   //     01 <no module>+3000
87   //     02 modZ+2000
88   //     03 base+4000
89   const ProcessedStack testStacks[] = {
90       MakeStack(moduleSet1, {100ul, 200ul, 300ul}),
91       MakeStack(moduleSet1, {1000ul, 2000ul, 3000ul}),
92       MakeStack(moduleSet2, {100ul, 200ul, 300ul, 400ul}),
93       MakeStack(moduleSet2, {1000ul, 2000ul, 3000ul, 4000ul}),
94   };
95 
96   // combined1 <-- testStacks[0] + testStacks[1]
97   // combined2 <-- testStacks[2] + testStacks[3]
98   CombinedStacks combined1, combined2;
99   combined1.AddStack(testStacks[0]);
100   combined1.AddStack(testStacks[1]);
101   combined2.AddStack(testStacks[2]);
102   combined2.AddStack(testStacks[3]);
103 
104   EXPECT_EQ(combined1.GetModuleCount(), mozilla::ArrayLength(moduleSet1));
105   EXPECT_EQ(combined1.GetStackCount(), 2u);
106   EXPECT_EQ(combined2.GetModuleCount(), mozilla::ArrayLength(moduleSet2) - 1);
107   EXPECT_EQ(combined2.GetStackCount(), 2u);
108 
109   // combined1 <-- combined1 + combined2
110   combined1.AddStacks(combined2);
111 
112   EXPECT_EQ(combined1.GetModuleCount(), 5u);  // {mod1, mod2, modX, modZ, base}
113   EXPECT_EQ(combined1.GetStackCount(), mozilla::ArrayLength(testStacks));
114 
115   for (size_t i = 0; i < combined1.GetStackCount(); ++i) {
116     const auto& expectedStack = testStacks[i];
117     const auto& actualStack = combined1.GetStack(i);
118     EXPECT_EQ(actualStack.size(), expectedStack.GetStackSize());
119     if (actualStack.size() != expectedStack.GetStackSize()) {
120       continue;
121     }
122 
123     for (size_t j = 0; j < actualStack.size(); ++j) {
124       const auto& expectedFrame = expectedStack.GetFrame(j);
125       const auto& actualFrame = actualStack[j];
126 
127       EXPECT_EQ(actualFrame.mOffset, expectedFrame.mOffset);
128 
129       if (expectedFrame.mModIndex == std::numeric_limits<uint16_t>::max()) {
130         EXPECT_EQ(actualFrame.mModIndex, std::numeric_limits<uint16_t>::max());
131       } else {
132         EXPECT_EQ(combined1.GetModule(actualFrame.mModIndex),
133                   expectedStack.GetModule(expectedFrame.mModIndex));
134       }
135     }
136   }
137 
138   // Only testStacks[3] will be stored into oneStack
139   CombinedStacks oneStack(1);
140   oneStack.AddStacks(combined1);
141 
142   EXPECT_EQ(oneStack.GetStackCount(), 1u);
143   EXPECT_EQ(oneStack.GetStack(0).size(), testStacks[3].GetStackSize());
144 
145   for (size_t i = 0; i < oneStack.GetStack(0).size(); ++i) {
146     const auto& expectedFrame = testStacks[3].GetFrame(i);
147     const auto& actualFrame = oneStack.GetStack(0)[i];
148 
149     EXPECT_EQ(actualFrame.mOffset, expectedFrame.mOffset);
150 
151     if (expectedFrame.mModIndex == std::numeric_limits<uint16_t>::max()) {
152       EXPECT_EQ(actualFrame.mModIndex, std::numeric_limits<uint16_t>::max());
153     } else {
154       EXPECT_EQ(oneStack.GetModule(actualFrame.mModIndex),
155                 testStacks[3].GetModule(expectedFrame.mModIndex));
156     }
157   }
158 }
159