Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "HangReports.h"
8 :
9 : namespace mozilla {
10 : namespace Telemetry {
11 :
12 : using namespace HangMonitor;
13 :
14 : // This utility function generates a string key that is used to index the annotations
15 : // in a hash map from |HangReports::AddHang|.
16 : nsresult
17 0 : ComputeAnnotationsKey(const HangAnnotationsPtr& aAnnotations, nsAString& aKeyOut)
18 : {
19 0 : UniquePtr<HangAnnotations::Enumerator> annotationsEnum = aAnnotations->GetEnumerator();
20 0 : if (!annotationsEnum) {
21 0 : return NS_ERROR_FAILURE;
22 : }
23 :
24 : // Append all the attributes to the key, to uniquely identify this annotation.
25 0 : nsAutoString key;
26 0 : nsAutoString value;
27 0 : while (annotationsEnum->Next(key, value)) {
28 0 : aKeyOut.Append(key);
29 0 : aKeyOut.Append(value);
30 : }
31 :
32 0 : return NS_OK;
33 : }
34 :
35 : #if defined(MOZ_GECKO_PROFILER)
36 : /** The maximum number of stacks that we're keeping for hang reports. */
37 : const size_t kMaxHangStacksKept = 50;
38 :
39 : void
40 0 : HangReports::AddHang(const Telemetry::ProcessedStack& aStack,
41 : uint32_t aDuration,
42 : int32_t aSystemUptime,
43 : int32_t aFirefoxUptime,
44 : HangAnnotationsPtr aAnnotations) {
45 : // Append the new stack to the stack's circular queue.
46 0 : size_t hangIndex = mStacks.AddStack(aStack);
47 : // Append the hang info at the same index, in mHangInfo.
48 0 : HangInfo info = { aDuration, aSystemUptime, aFirefoxUptime };
49 0 : if (mHangInfo.size() < kMaxHangStacksKept) {
50 0 : mHangInfo.push_back(info);
51 : } else {
52 0 : mHangInfo[hangIndex] = info;
53 : // Remove any reference to the stack overwritten in the circular queue
54 : // from the annotations.
55 0 : PruneStackReferences(hangIndex);
56 : }
57 :
58 0 : if (!aAnnotations) {
59 0 : return;
60 : }
61 :
62 0 : nsAutoString annotationsKey;
63 : // Generate a key to index aAnnotations in the hash map.
64 0 : nsresult rv = ComputeAnnotationsKey(aAnnotations, annotationsKey);
65 0 : if (NS_FAILED(rv)) {
66 0 : return;
67 : }
68 :
69 0 : AnnotationInfo* annotationsEntry = mAnnotationInfo.Get(annotationsKey);
70 0 : if (annotationsEntry) {
71 : // If the key is already in the hash map, append the index of the chrome hang
72 : // to its indices.
73 0 : annotationsEntry->mHangIndices.AppendElement(hangIndex);
74 0 : return;
75 : }
76 :
77 : // If the key was not found, add the annotations to the hash map.
78 0 : mAnnotationInfo.Put(annotationsKey, new AnnotationInfo(hangIndex, Move(aAnnotations)));
79 : }
80 :
81 : /**
82 : * This function removes links to discarded chrome hangs stacks and prunes unused
83 : * annotations.
84 : */
85 : void
86 0 : HangReports::PruneStackReferences(const size_t aRemovedStackIndex) {
87 : // We need to adjust the indices that link annotations to chrome hangs. Since we
88 : // removed a stack, we must remove all references to it and prune annotations
89 : // linked to no stacks.
90 0 : for (auto iter = mAnnotationInfo.Iter(); !iter.Done(); iter.Next()) {
91 0 : nsTArray<uint32_t>& stackIndices = iter.Data()->mHangIndices;
92 0 : size_t toRemove = stackIndices.NoIndex;
93 0 : for (size_t k = 0; k < stackIndices.Length(); k++) {
94 : // Is this index referencing the removed stack?
95 0 : if (stackIndices[k] == aRemovedStackIndex) {
96 0 : toRemove = k;
97 0 : break;
98 : }
99 : }
100 :
101 : // Remove the index referencing the old stack from the annotation.
102 0 : if (toRemove != stackIndices.NoIndex) {
103 0 : stackIndices.RemoveElementAt(toRemove);
104 : }
105 :
106 : // If this annotation no longer references any stack, drop it.
107 0 : if (!stackIndices.Length()) {
108 0 : iter.Remove();
109 : }
110 : }
111 0 : }
112 : #endif
113 :
114 : size_t
115 0 : HangReports::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
116 0 : size_t n = 0;
117 0 : n += mStacks.SizeOfExcludingThis();
118 : // This is a crude approximation. See comment on
119 : // CombinedStacks::SizeOfExcludingThis.
120 0 : n += mHangInfo.capacity() * sizeof(HangInfo);
121 0 : n += mAnnotationInfo.ShallowSizeOfExcludingThis(aMallocSizeOf);
122 0 : n += mAnnotationInfo.Count() * sizeof(AnnotationInfo);
123 0 : for (auto iter = mAnnotationInfo.ConstIter(); !iter.Done(); iter.Next()) {
124 0 : n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
125 0 : n += iter.Data()->mAnnotations->SizeOfIncludingThis(aMallocSizeOf);
126 : }
127 0 : return n;
128 : }
129 :
130 : const CombinedStacks&
131 0 : HangReports::GetStacks() const {
132 0 : return mStacks;
133 : }
134 :
135 : uint32_t
136 0 : HangReports::GetDuration(unsigned aIndex) const {
137 0 : return mHangInfo[aIndex].mDuration;
138 : }
139 :
140 : int32_t
141 0 : HangReports::GetSystemUptime(unsigned aIndex) const {
142 0 : return mHangInfo[aIndex].mSystemUptime;
143 : }
144 :
145 : int32_t
146 0 : HangReports::GetFirefoxUptime(unsigned aIndex) const {
147 0 : return mHangInfo[aIndex].mFirefoxUptime;
148 : }
149 :
150 : const nsClassHashtable<nsStringHashKey, HangReports::AnnotationInfo>&
151 0 : HangReports::GetAnnotationInfo() const {
152 0 : return mAnnotationInfo;
153 : }
154 :
155 : } // namespace Telemetry
156 : } // namespace mozilla
|