/src/mozilla-central/toolkit/components/telemetry/other/TelemetryIOInterposeObserver.cpp
Line | Count | Source (jump to first uncovered line) |
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 "TelemetryIOInterposeObserver.h" |
8 | | #include "core/TelemetryCommon.h" |
9 | | |
10 | | namespace mozilla { |
11 | | namespace Telemetry { |
12 | | |
13 | | TelemetryIOInterposeObserver::TelemetryIOInterposeObserver(nsIFile* aXreDir) |
14 | | : mCurStage(STAGE_STARTUP) |
15 | 3 | { |
16 | 3 | nsAutoString xreDirPath; |
17 | 3 | nsresult rv = aXreDir->GetPath(xreDirPath); |
18 | 3 | if (NS_SUCCEEDED(rv)) { |
19 | 3 | AddPath(xreDirPath, NS_LITERAL_STRING("{xre}")); |
20 | 3 | } |
21 | 3 | } |
22 | | |
23 | | void TelemetryIOInterposeObserver::AddPath(const nsAString& aPath, |
24 | | const nsAString& aSubstName) |
25 | 3 | { |
26 | 3 | mSafeDirs.AppendElement(SafeDir(aPath, aSubstName)); |
27 | 3 | } |
28 | | |
29 | | // Threshold for reporting slow main-thread I/O (50 milliseconds). |
30 | | const TimeDuration kTelemetryReportThreshold = TimeDuration::FromMilliseconds(50); |
31 | | |
32 | | void TelemetryIOInterposeObserver::Observe(Observation& aOb) |
33 | 54 | { |
34 | 54 | // We only report main-thread I/O |
35 | 54 | if (!IsMainThread()) { |
36 | 0 | return; |
37 | 0 | } |
38 | 54 | |
39 | 54 | if (aOb.ObservedOperation() == OpNextStage) { |
40 | 0 | mCurStage = NextStage(mCurStage); |
41 | 0 | MOZ_ASSERT(mCurStage < NUM_STAGES); |
42 | 0 | return; |
43 | 0 | } |
44 | 54 | |
45 | 54 | if (aOb.Duration() < kTelemetryReportThreshold) { |
46 | 54 | return; |
47 | 54 | } |
48 | 0 | |
49 | 0 | // Get the filename |
50 | 0 | nsAutoString filename; |
51 | 0 | aOb.Filename(filename); |
52 | 0 |
|
53 | 0 | // Discard observations without filename |
54 | 0 | if (filename.IsEmpty()) { |
55 | 0 | return; |
56 | 0 | } |
57 | 0 | |
58 | | #if defined(XP_WIN) |
59 | | nsCaseInsensitiveStringComparator comparator; |
60 | | #else |
61 | 0 | nsDefaultStringComparator comparator; |
62 | 0 | #endif |
63 | 0 | nsAutoString processedName; |
64 | 0 | uint32_t safeDirsLen = mSafeDirs.Length(); |
65 | 0 | for (uint32_t i = 0; i < safeDirsLen; ++i) { |
66 | 0 | if (StringBeginsWith(filename, mSafeDirs[i].mPath, comparator)) { |
67 | 0 | processedName = mSafeDirs[i].mSubstName; |
68 | 0 | processedName += Substring(filename, mSafeDirs[i].mPath.Length()); |
69 | 0 | break; |
70 | 0 | } |
71 | 0 | } |
72 | 0 |
|
73 | 0 | if (processedName.IsEmpty()) { |
74 | 0 | return; |
75 | 0 | } |
76 | 0 | |
77 | 0 | // Create a new entry or retrieve the existing one |
78 | 0 | FileIOEntryType* entry = mFileStats.PutEntry(processedName); |
79 | 0 | if (entry) { |
80 | 0 | FileStats& stats = entry->mData.mStats[mCurStage]; |
81 | 0 | // Update the statistics |
82 | 0 | stats.totalTime += (double) aOb.Duration().ToMilliseconds(); |
83 | 0 | switch (aOb.ObservedOperation()) { |
84 | 0 | case OpCreateOrOpen: |
85 | 0 | stats.creates++; |
86 | 0 | break; |
87 | 0 | case OpRead: |
88 | 0 | stats.reads++; |
89 | 0 | break; |
90 | 0 | case OpWrite: |
91 | 0 | stats.writes++; |
92 | 0 | break; |
93 | 0 | case OpFSync: |
94 | 0 | stats.fsyncs++; |
95 | 0 | break; |
96 | 0 | case OpStat: |
97 | 0 | stats.stats++; |
98 | 0 | break; |
99 | 0 | default: |
100 | 0 | break; |
101 | 0 | } |
102 | 0 | } |
103 | 0 | } |
104 | | |
105 | | bool TelemetryIOInterposeObserver::ReflectFileStats(FileIOEntryType* entry, |
106 | | JSContext *cx, |
107 | | JS::Handle<JSObject*> obj) |
108 | 0 | { |
109 | 0 | JS::AutoValueArray<NUM_STAGES> stages(cx); |
110 | 0 |
|
111 | 0 | FileStatsByStage& statsByStage = entry->mData; |
112 | 0 | for (int s = STAGE_STARTUP; s < NUM_STAGES; ++s) { |
113 | 0 | FileStats& fileStats = statsByStage.mStats[s]; |
114 | 0 |
|
115 | 0 | if (fileStats.totalTime == 0 && fileStats.creates == 0 && |
116 | 0 | fileStats.reads == 0 && fileStats.writes == 0 && |
117 | 0 | fileStats.fsyncs == 0 && fileStats.stats == 0) { |
118 | 0 | // Don't add an array that contains no information |
119 | 0 | stages[s].setNull(); |
120 | 0 | continue; |
121 | 0 | } |
122 | 0 | |
123 | 0 | // Array we want to report |
124 | 0 | JS::AutoValueArray<6> stats(cx); |
125 | 0 | stats[0].setNumber(fileStats.totalTime); |
126 | 0 | stats[1].setNumber(fileStats.creates); |
127 | 0 | stats[2].setNumber(fileStats.reads); |
128 | 0 | stats[3].setNumber(fileStats.writes); |
129 | 0 | stats[4].setNumber(fileStats.fsyncs); |
130 | 0 | stats[5].setNumber(fileStats.stats); |
131 | 0 |
|
132 | 0 | // Create jsStats as array of elements above |
133 | 0 | JS::RootedObject jsStats(cx, JS_NewArrayObject(cx, stats)); |
134 | 0 | if (!jsStats) { |
135 | 0 | continue; |
136 | 0 | } |
137 | 0 | |
138 | 0 | stages[s].setObject(*jsStats); |
139 | 0 | } |
140 | 0 |
|
141 | 0 | JS::Rooted<JSObject*> jsEntry(cx, JS_NewArrayObject(cx, stages)); |
142 | 0 | if (!jsEntry) { |
143 | 0 | return false; |
144 | 0 | } |
145 | 0 | |
146 | 0 | // Add jsEntry to top-level dictionary |
147 | 0 | const nsAString& key = entry->GetKey(); |
148 | 0 | return JS_DefineUCProperty(cx, obj, key.Data(), key.Length(), |
149 | 0 | jsEntry, JSPROP_ENUMERATE | JSPROP_READONLY); |
150 | 0 | } |
151 | | |
152 | | bool TelemetryIOInterposeObserver::ReflectIntoJS(JSContext *cx, |
153 | | JS::Handle<JSObject*> rootObj) |
154 | 0 | { |
155 | 0 | return mFileStats.ReflectIntoJS(ReflectFileStats, cx, rootObj); |
156 | 0 | } |
157 | | |
158 | | /** |
159 | | * Get size of hash table with file stats |
160 | | */ |
161 | | |
162 | | size_t TelemetryIOInterposeObserver::SizeOfIncludingThis |
163 | | (mozilla::MallocSizeOf aMallocSizeOf) const |
164 | 0 | { |
165 | 0 | return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); |
166 | 0 | } |
167 | | |
168 | | size_t TelemetryIOInterposeObserver::SizeOfExcludingThis |
169 | | (mozilla::MallocSizeOf aMallocSizeOf) const |
170 | 0 | { |
171 | 0 | size_t size = 0; |
172 | 0 | size += mFileStats.ShallowSizeOfExcludingThis(aMallocSizeOf); |
173 | 0 | for (auto iter = mFileStats.ConstIter(); !iter.Done(); iter.Next()) { |
174 | 0 | size += iter.Get()->GetKey().SizeOfExcludingThisIfUnshared(aMallocSizeOf); |
175 | 0 | } |
176 | 0 | size += mSafeDirs.ShallowSizeOfExcludingThis(aMallocSizeOf); |
177 | 0 | uint32_t safeDirsLen = mSafeDirs.Length(); |
178 | 0 | for (uint32_t i = 0; i < safeDirsLen; ++i) { |
179 | 0 | size += mSafeDirs[i].SizeOfExcludingThis(aMallocSizeOf); |
180 | 0 | } |
181 | 0 | return size; |
182 | 0 | } |
183 | | |
184 | | } // namespace Telemetry |
185 | | } // namespace mozilla |