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