1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
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 /* Various JS utility functions. */
8
9 #include "jsutil.h"
10
11 #include "mozilla/Assertions.h"
12 #include "mozilla/MathAlgorithms.h"
13 #include "mozilla/Maybe.h"
14 #include "mozilla/PodOperations.h"
15 #include "mozilla/ThreadLocal.h"
16
17 #include <stdio.h>
18
19 #include "jstypes.h"
20
21 #include "js/Utility.h"
22 #include "util/Windows.h"
23 #include "vm/HelperThreads.h"
24
25 using namespace js;
26
27 using mozilla::CeilingLog2Size;
28 using mozilla::PodArrayZero;
29
30 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
31 /* For OOM testing functionality in Utility.h. */
32 namespace js {
33
34 mozilla::Atomic<AutoEnterOOMUnsafeRegion*> AutoEnterOOMUnsafeRegion::owner_;
35
36 namespace oom {
37
38 JS_PUBLIC_DATA uint32_t targetThread = 0;
39 MOZ_THREAD_LOCAL(uint32_t) threadType;
40 JS_PUBLIC_DATA uint64_t maxAllocations = UINT64_MAX;
41 JS_PUBLIC_DATA uint64_t counter = 0;
42 JS_PUBLIC_DATA bool failAlways = true;
43
44 JS_PUBLIC_DATA uint32_t stackTargetThread = 0;
45 JS_PUBLIC_DATA uint64_t maxStackChecks = UINT64_MAX;
46 JS_PUBLIC_DATA uint64_t stackCheckCounter = 0;
47 JS_PUBLIC_DATA bool stackCheckFailAlways = true;
48
49 JS_PUBLIC_DATA uint32_t interruptTargetThread = 0;
50 JS_PUBLIC_DATA uint64_t maxInterruptChecks = UINT64_MAX;
51 JS_PUBLIC_DATA uint64_t interruptCheckCounter = 0;
52 JS_PUBLIC_DATA bool interruptCheckFailAlways = true;
53
InitThreadType(void)54 bool InitThreadType(void) { return threadType.init(); }
55
SetThreadType(ThreadType type)56 void SetThreadType(ThreadType type) { threadType.set(type); }
57
GetThreadType(void)58 uint32_t GetThreadType(void) { return threadType.get(); }
59
IsHelperThreadType(uint32_t thread)60 static inline bool IsHelperThreadType(uint32_t thread) {
61 return thread != THREAD_TYPE_NONE && thread != THREAD_TYPE_COOPERATING;
62 }
63
SimulateOOMAfter(uint64_t allocations,uint32_t thread,bool always)64 void SimulateOOMAfter(uint64_t allocations, uint32_t thread, bool always) {
65 Maybe<AutoLockHelperThreadState> lock;
66 if (IsHelperThreadType(targetThread) || IsHelperThreadType(thread)) {
67 lock.emplace();
68 HelperThreadState().waitForAllThreadsLocked(lock.ref());
69 }
70
71 MOZ_ASSERT(counter + allocations > counter);
72 MOZ_ASSERT(thread > js::THREAD_TYPE_NONE && thread < js::THREAD_TYPE_MAX);
73 targetThread = thread;
74 maxAllocations = counter + allocations;
75 failAlways = always;
76 }
77
ResetSimulatedOOM()78 void ResetSimulatedOOM() {
79 Maybe<AutoLockHelperThreadState> lock;
80 if (IsHelperThreadType(targetThread)) {
81 lock.emplace();
82 HelperThreadState().waitForAllThreadsLocked(lock.ref());
83 }
84
85 targetThread = THREAD_TYPE_NONE;
86 maxAllocations = UINT64_MAX;
87 failAlways = false;
88 }
89
SimulateStackOOMAfter(uint64_t checks,uint32_t thread,bool always)90 void SimulateStackOOMAfter(uint64_t checks, uint32_t thread, bool always) {
91 Maybe<AutoLockHelperThreadState> lock;
92 if (IsHelperThreadType(stackTargetThread) || IsHelperThreadType(thread)) {
93 lock.emplace();
94 HelperThreadState().waitForAllThreadsLocked(lock.ref());
95 }
96
97 MOZ_ASSERT(stackCheckCounter + checks > stackCheckCounter);
98 MOZ_ASSERT(thread > js::THREAD_TYPE_NONE && thread < js::THREAD_TYPE_MAX);
99 stackTargetThread = thread;
100 maxStackChecks = stackCheckCounter + checks;
101 stackCheckFailAlways = always;
102 }
103
ResetSimulatedStackOOM()104 void ResetSimulatedStackOOM() {
105 Maybe<AutoLockHelperThreadState> lock;
106 if (IsHelperThreadType(stackTargetThread)) {
107 lock.emplace();
108 HelperThreadState().waitForAllThreadsLocked(lock.ref());
109 }
110
111 stackTargetThread = THREAD_TYPE_NONE;
112 maxStackChecks = UINT64_MAX;
113 stackCheckFailAlways = false;
114 }
115
SimulateInterruptAfter(uint64_t checks,uint32_t thread,bool always)116 void SimulateInterruptAfter(uint64_t checks, uint32_t thread, bool always) {
117 Maybe<AutoLockHelperThreadState> lock;
118 if (IsHelperThreadType(interruptTargetThread) || IsHelperThreadType(thread)) {
119 lock.emplace();
120 HelperThreadState().waitForAllThreadsLocked(lock.ref());
121 }
122
123 MOZ_ASSERT(interruptCheckCounter + checks > interruptCheckCounter);
124 MOZ_ASSERT(thread > js::THREAD_TYPE_NONE && thread < js::THREAD_TYPE_MAX);
125 interruptTargetThread = thread;
126 maxInterruptChecks = interruptCheckCounter + checks;
127 interruptCheckFailAlways = always;
128 }
129
ResetSimulatedInterrupt()130 void ResetSimulatedInterrupt() {
131 Maybe<AutoLockHelperThreadState> lock;
132 if (IsHelperThreadType(interruptTargetThread)) {
133 lock.emplace();
134 HelperThreadState().waitForAllThreadsLocked(lock.ref());
135 }
136
137 interruptTargetThread = THREAD_TYPE_NONE;
138 maxInterruptChecks = UINT64_MAX;
139 interruptCheckFailAlways = false;
140 }
141
142 } // namespace oom
143 } // namespace js
144 #endif // defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
145
146 JS_PUBLIC_DATA arena_id_t js::MallocArena;
147
InitMallocAllocator()148 void js::InitMallocAllocator() { MallocArena = moz_create_arena(); }
149
ShutDownMallocAllocator()150 void js::ShutDownMallocAllocator() {
151 // Until Bug 1364359 is fixed it is unsafe to call moz_dispose_arena.
152 // moz_dispose_arena(MallocArena);
153 }
154
JS_Assert(const char * s,const char * file,int ln)155 JS_PUBLIC_API void JS_Assert(const char* s, const char* file, int ln) {
156 MOZ_ReportAssertionFailure(s, file, ln);
157 MOZ_CRASH();
158 }
159
160 #ifdef __linux__
161
162 #include <malloc.h>
163 #include <stdlib.h>
164
165 namespace js {
166
167 // This function calls all the vanilla heap allocation functions. It is never
168 // called, and exists purely to help config/check_vanilla_allocations.py. See
169 // that script for more details.
AllTheNonBasicVanillaNewAllocations()170 extern MOZ_COLD void AllTheNonBasicVanillaNewAllocations() {
171 // posix_memalign and aligned_alloc aren't available on all Linux
172 // configurations.
173 // valloc was deprecated in Android 5.0
174 // char* q;
175 // posix_memalign((void**)&q, 16, 16);
176
177 intptr_t p = intptr_t(malloc(16)) + intptr_t(calloc(1, 16)) +
178 intptr_t(realloc(nullptr, 16)) + intptr_t(new char) +
179 intptr_t(new char) + intptr_t(new char) +
180 intptr_t(new char[16]) + intptr_t(memalign(16, 16)) +
181 // intptr_t(q) +
182 // intptr_t(aligned_alloc(16, 16)) +
183 // intptr_t(valloc(4096)) +
184 intptr_t(strdup("dummy"));
185
186 printf("%u\n", uint32_t(p)); // make sure |p| is not optimized away
187
188 free((int*)p); // this would crash if ever actually called
189
190 MOZ_CRASH();
191 }
192
193 } // namespace js
194
195 #endif // __linux__
196
197 #ifdef JS_BASIC_STATS
198
199 #include <math.h>
200
201 /*
202 * Histogram bins count occurrences of values <= the bin label, as follows:
203 *
204 * linear: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 or more
205 * 2**x: 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 or more
206 * 10**x: 0, 1, 10, 100, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9 or more
207 *
208 * We wish to count occurrences of 0 and 1 values separately, always.
209 */
BinToVal(unsigned logscale,unsigned bin)210 static uint32_t BinToVal(unsigned logscale, unsigned bin) {
211 MOZ_ASSERT(bin <= 10);
212 if (bin <= 1 || logscale == 0) return bin;
213 --bin;
214 if (logscale == 2) return JS_BIT(bin);
215 MOZ_ASSERT(logscale == 10);
216 return uint32_t(pow(10.0, (double)bin));
217 }
218
ValToBin(unsigned logscale,uint32_t val)219 static unsigned ValToBin(unsigned logscale, uint32_t val) {
220 unsigned bin;
221
222 if (val <= 1) return val;
223 bin = (logscale == 10)
224 ? (unsigned)ceil(log10((double)val))
225 : (logscale == 2) ? (unsigned)CeilingLog2Size(val) : val;
226 return Min(bin, 10U);
227 }
228
JS_BasicStatsAccum(JSBasicStats * bs,uint32_t val)229 void JS_BasicStatsAccum(JSBasicStats* bs, uint32_t val) {
230 unsigned oldscale, newscale, bin;
231 double mean;
232
233 ++bs->num;
234 if (bs->max < val) bs->max = val;
235 bs->sum += val;
236 bs->sqsum += (double)val * val;
237
238 oldscale = bs->logscale;
239 if (oldscale != 10) {
240 mean = bs->sum / bs->num;
241 if (bs->max > 16 && mean > 8) {
242 newscale = (bs->max > 1e6 && mean > 1000) ? 10 : 2;
243 if (newscale != oldscale) {
244 uint32_t newhist[11], newbin;
245
246 PodArrayZero(newhist);
247 for (bin = 0; bin <= 10; bin++) {
248 newbin = ValToBin(newscale, BinToVal(oldscale, bin));
249 newhist[newbin] += bs->hist[bin];
250 }
251 js_memcpy(bs->hist, newhist, sizeof bs->hist);
252 bs->logscale = newscale;
253 }
254 }
255 }
256
257 bin = ValToBin(bs->logscale, val);
258 ++bs->hist[bin];
259 }
260
JS_MeanAndStdDev(uint32_t num,double sum,double sqsum,double * sigma)261 double JS_MeanAndStdDev(uint32_t num, double sum, double sqsum, double* sigma) {
262 double var;
263
264 if (num == 0 || sum == 0) {
265 *sigma = 0;
266 return 0;
267 }
268
269 var = num * sqsum - sum * sum;
270 if (var < 0 || num == 1)
271 var = 0;
272 else
273 var /= (double)num * (num - 1);
274
275 /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */
276 *sigma = (var != 0) ? sqrt(var) : 0;
277 return sum / num;
278 }
279
JS_DumpBasicStats(JSBasicStats * bs,const char * title,FILE * fp)280 void JS_DumpBasicStats(JSBasicStats* bs, const char* title, FILE* fp) {
281 double mean, sigma;
282
283 mean = JS_MeanAndStdDevBS(bs, &sigma);
284 fprintf(fp, "\nmean %s %g, std. deviation %g, max %lu\n", title, mean, sigma,
285 (unsigned long)bs->max);
286 JS_DumpHistogram(bs, fp);
287 }
288
JS_DumpHistogram(JSBasicStats * bs,FILE * fp)289 void JS_DumpHistogram(JSBasicStats* bs, FILE* fp) {
290 unsigned bin;
291 uint32_t cnt, max;
292 double sum, mean;
293
294 for (bin = 0, max = 0, sum = 0; bin <= 10; bin++) {
295 cnt = bs->hist[bin];
296 if (max < cnt) max = cnt;
297 sum += cnt;
298 }
299 mean = sum / cnt;
300 for (bin = 0; bin <= 10; bin++) {
301 unsigned val = BinToVal(bs->logscale, bin);
302 unsigned end = (bin == 10) ? 0 : BinToVal(bs->logscale, bin + 1);
303 cnt = bs->hist[bin];
304 if (val + 1 == end)
305 fprintf(fp, " [%6u]", val);
306 else if (end != 0)
307 fprintf(fp, "[%6u, %6u]", val, end - 1);
308 else
309 fprintf(fp, "[%6u, +inf]", val);
310 fprintf(fp, ": %8u ", cnt);
311 if (cnt != 0) {
312 if (max > 1e6 && mean > 1e3)
313 cnt = uint32_t(ceil(log10((double)cnt)));
314 else if (max > 16 && mean > 8)
315 cnt = CeilingLog2Size(cnt);
316 for (unsigned i = 0; i < cnt; i++) putc('*', fp);
317 }
318 putc('\n', fp);
319 }
320 }
321
322 #endif /* JS_BASIC_STATS */
323