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 "mozilla/AvailableMemoryTracker.h"
8 :
9 : #if defined(XP_WIN)
10 : #include "prinrval.h"
11 : #include "prenv.h"
12 : #include "nsExceptionHandler.h"
13 : #include "nsIMemoryReporter.h"
14 : #include "nsMemoryPressure.h"
15 : #include "nsPrintfCString.h"
16 : #endif
17 :
18 : #include "nsIObserver.h"
19 : #include "nsIObserverService.h"
20 : #include "nsIRunnable.h"
21 : #include "nsISupports.h"
22 : #include "nsITimer.h"
23 : #include "nsThreadUtils.h"
24 : #include "nsXULAppAPI.h"
25 :
26 : #include "mozilla/ResultExtensions.h"
27 : #include "mozilla/Services.h"
28 : #include "mozilla/Unused.h"
29 :
30 : #if defined(XP_WIN)
31 : # include "nsWindowsDllInterceptor.h"
32 : # include <windows.h>
33 : #endif
34 :
35 : #if defined(MOZ_MEMORY)
36 : # include "mozmemory.h"
37 : #endif // MOZ_MEMORY
38 :
39 : using namespace mozilla;
40 :
41 : namespace {
42 :
43 : #if defined(XP_WIN)
44 :
45 : // Fire a low-memory notification if we have less than this many bytes of
46 : // virtual address space available.
47 : #if defined(HAVE_64BIT_BUILD)
48 : static const size_t kLowVirtualMemoryThreshold = 0;
49 : #else
50 : static const size_t kLowVirtualMemoryThreshold = 256 * 1024 * 1024;
51 : #endif
52 :
53 : // Fire a low-memory notification if we have less than this many bytes of commit
54 : // space (physical memory plus page file) left.
55 : static const size_t kLowCommitSpaceThreshold = 256 * 1024 * 1024;
56 :
57 : // Fire a low-memory notification if we have less than this many bytes of
58 : // physical memory available on the whole machine.
59 : static const size_t kLowPhysicalMemoryThreshold = 0;
60 :
61 : // Don't fire a low-memory notification because of low available physical
62 : // memory or low commit space more often than this interval.
63 : static const uint32_t kLowMemoryNotificationIntervalMS = 10000;
64 :
65 : Atomic<uint32_t, MemoryOrdering::Relaxed> sNumLowVirtualMemEvents;
66 : Atomic<uint32_t, MemoryOrdering::Relaxed> sNumLowCommitSpaceEvents;
67 : Atomic<uint32_t, MemoryOrdering::Relaxed> sNumLowPhysicalMemEvents;
68 :
69 : #if !defined(HAVE_64BIT_BUILD)
70 :
71 : WindowsDllInterceptor sKernel32Intercept;
72 : WindowsDllInterceptor sGdi32Intercept;
73 :
74 : // Has Init() been called?
75 : bool sInitialized = false;
76 :
77 : // Has Activate() been called? The hooks don't do anything until this happens.
78 : bool sHooksActive = false;
79 :
80 : // Alas, we'd like to use mozilla::TimeStamp, but we can't, because it acquires
81 : // a lock!
82 : volatile bool sUnderMemoryPressure = false;
83 : volatile PRIntervalTime sLastLowMemoryNotificationTime;
84 :
85 : // These are function pointers to the functions we wrap in Init().
86 :
87 : void* (WINAPI* sVirtualAllocOrig)(LPVOID aAddress, SIZE_T aSize,
88 : DWORD aAllocationType, DWORD aProtect);
89 :
90 : void* (WINAPI* sMapViewOfFileOrig)(HANDLE aFileMappingObject,
91 : DWORD aDesiredAccess, DWORD aFileOffsetHigh,
92 : DWORD aFileOffsetLow, SIZE_T aNumBytesToMap);
93 :
94 : HBITMAP(WINAPI* sCreateDIBSectionOrig)(HDC aDC, const BITMAPINFO* aBitmapInfo,
95 : UINT aUsage, VOID** aBits,
96 : HANDLE aSection, DWORD aOffset);
97 :
98 : /**
99 : * Fire a memory pressure event if we were not under memory pressure yet, or
100 : * fire an ongoing one if it's been long enough since the last one we
101 : * fired.
102 : */
103 : bool
104 : MaybeScheduleMemoryPressureEvent()
105 : {
106 : MemoryPressureState state = MemPressure_New;
107 : PRIntervalTime now = PR_IntervalNow();
108 :
109 : // If this interval rolls over, we may fire an extra memory pressure
110 : // event, but that's not a big deal.
111 : PRIntervalTime interval = now - sLastLowMemoryNotificationTime;
112 : if (sUnderMemoryPressure) {
113 : if (PR_IntervalToMilliseconds(interval) <
114 : kLowMemoryNotificationIntervalMS) {
115 : return false;
116 : }
117 :
118 : state = MemPressure_Ongoing;
119 : }
120 :
121 : // There's a bit of a race condition here, since an interval may be a
122 : // 64-bit number, and 64-bit writes aren't atomic on x86-32. But let's
123 : // not worry about it -- the races only happen when we're already
124 : // experiencing memory pressure and firing notifications, so the worst
125 : // thing that can happen is that we fire two notifications when we
126 : // should have fired only one.
127 : sUnderMemoryPressure = true;
128 : sLastLowMemoryNotificationTime = now;
129 :
130 : NS_DispatchEventualMemoryPressure(state);
131 : return true;
132 : }
133 :
134 : static bool
135 : CheckLowMemory(DWORDLONG available, size_t threshold,
136 : Atomic<uint32_t, MemoryOrdering::Relaxed>& counter)
137 : {
138 : if (available < threshold) {
139 : if (MaybeScheduleMemoryPressureEvent()) {
140 : counter++;
141 : }
142 :
143 : return true;
144 : }
145 :
146 : return false;
147 : }
148 :
149 : void
150 : CheckMemAvailable()
151 : {
152 : if (!sHooksActive) {
153 : return;
154 : }
155 :
156 : MEMORYSTATUSEX stat;
157 : stat.dwLength = sizeof(stat);
158 : bool success = GlobalMemoryStatusEx(&stat);
159 :
160 : if (success) {
161 : bool lowMemory = CheckLowMemory(stat.ullAvailVirtual,
162 : kLowVirtualMemoryThreshold,
163 : sNumLowVirtualMemEvents);
164 : lowMemory |= CheckLowMemory(stat.ullAvailPageFile, kLowCommitSpaceThreshold,
165 : sNumLowCommitSpaceEvents);
166 : lowMemory |= CheckLowMemory(stat.ullAvailPhys, kLowPhysicalMemoryThreshold,
167 : sNumLowPhysicalMemEvents);
168 :
169 : sUnderMemoryPressure = lowMemory;
170 : }
171 : }
172 :
173 : LPVOID WINAPI
174 : VirtualAllocHook(LPVOID aAddress, SIZE_T aSize,
175 : DWORD aAllocationType,
176 : DWORD aProtect)
177 : {
178 : // It's tempting to see whether we have enough free virtual address space for
179 : // this allocation and, if we don't, synchronously fire a low-memory
180 : // notification to free some before we allocate.
181 : //
182 : // Unfortunately that doesn't work, principally because code doesn't expect a
183 : // call to malloc could trigger a GC (or call into the other routines which
184 : // are triggered by a low-memory notification).
185 : //
186 : // I think the best we can do here is try to allocate the memory and check
187 : // afterwards how much free virtual address space we have. If we're running
188 : // low, we schedule a low-memory notification to run as soon as possible.
189 :
190 : LPVOID result = sVirtualAllocOrig(aAddress, aSize, aAllocationType, aProtect);
191 :
192 : // Don't call CheckMemAvailable for MEM_RESERVE if we're not tracking low
193 : // virtual memory. Similarly, don't call CheckMemAvailable for MEM_COMMIT if
194 : // we're not tracking low physical memory.
195 : if ((kLowVirtualMemoryThreshold != 0 && aAllocationType & MEM_RESERVE) ||
196 : ((kLowCommitSpaceThreshold != 0 || kLowPhysicalMemoryThreshold != 0) &&
197 : aAllocationType & MEM_COMMIT)) {
198 : CheckMemAvailable();
199 : }
200 :
201 : return result;
202 : }
203 :
204 : LPVOID WINAPI
205 : MapViewOfFileHook(HANDLE aFileMappingObject,
206 : DWORD aDesiredAccess,
207 : DWORD aFileOffsetHigh,
208 : DWORD aFileOffsetLow,
209 : SIZE_T aNumBytesToMap)
210 : {
211 : LPVOID result = sMapViewOfFileOrig(aFileMappingObject, aDesiredAccess,
212 : aFileOffsetHigh, aFileOffsetLow,
213 : aNumBytesToMap);
214 : CheckMemAvailable();
215 : return result;
216 : }
217 :
218 : HBITMAP WINAPI
219 : CreateDIBSectionHook(HDC aDC,
220 : const BITMAPINFO* aBitmapInfo,
221 : UINT aUsage,
222 : VOID** aBits,
223 : HANDLE aSection,
224 : DWORD aOffset)
225 : {
226 : // There are a lot of calls to CreateDIBSection, so we make some effort not
227 : // to CheckMemAvailable() for calls to CreateDIBSection which allocate only
228 : // a small amount of memory.
229 :
230 : // If aSection is non-null, CreateDIBSection won't allocate any new memory.
231 : bool doCheck = false;
232 : if (sHooksActive && !aSection && aBitmapInfo) {
233 : uint16_t bitCount = aBitmapInfo->bmiHeader.biBitCount;
234 : if (bitCount == 0) {
235 : // MSDN says bitCount == 0 means that it figures out how many bits each
236 : // pixel gets by examining the corresponding JPEG or PNG data. We'll just
237 : // assume the worst.
238 : bitCount = 32;
239 : }
240 :
241 : // |size| contains the expected allocation size in *bits*. Height may be
242 : // negative (indicating the direction the DIB is drawn in), so we take the
243 : // absolute value.
244 : int64_t size = bitCount * aBitmapInfo->bmiHeader.biWidth *
245 : aBitmapInfo->bmiHeader.biHeight;
246 : if (size < 0) {
247 : size *= -1;
248 : }
249 :
250 : // If we're allocating more than 1MB, check how much memory is left after
251 : // the allocation.
252 : if (size > 1024 * 1024 * 8) {
253 : doCheck = true;
254 : }
255 : }
256 :
257 : HBITMAP result = sCreateDIBSectionOrig(aDC, aBitmapInfo, aUsage, aBits,
258 : aSection, aOffset);
259 :
260 : if (doCheck) {
261 : CheckMemAvailable();
262 : }
263 :
264 : return result;
265 : }
266 :
267 : #else
268 :
269 : class nsAvailableMemoryWatcher final : public nsIObserver,
270 : public nsITimerCallback
271 : {
272 : public:
273 : NS_DECL_ISUPPORTS
274 : NS_DECL_NSIOBSERVER
275 : NS_DECL_NSITIMERCALLBACK
276 :
277 : nsresult Init();
278 :
279 : private:
280 : // Poll the amount of free memory at this rate.
281 : static const uint32_t kPollingIntervalMS = 1000;
282 :
283 : // Observer topics we subscribe to
284 : static const char* const kObserverTopics[];
285 :
286 : static bool IsVirtualMemoryLow(const MEMORYSTATUSEX& aStat);
287 : static bool IsCommitSpaceLow(const MEMORYSTATUSEX& aStat);
288 : static bool IsPhysicalMemoryLow(const MEMORYSTATUSEX& aStat);
289 :
290 : ~nsAvailableMemoryWatcher() {};
291 : void AdjustPollingInterval(const bool aLowMemory);
292 : void SendMemoryPressureEvent();
293 : void Shutdown();
294 :
295 : nsCOMPtr<nsITimer> mTimer;
296 : bool mUnderMemoryPressure;
297 : };
298 :
299 : const char* const nsAvailableMemoryWatcher::kObserverTopics[] = {
300 : "quit-application",
301 : "user-interaction-active",
302 : "user-interaction-inactive",
303 : };
304 :
305 : NS_IMPL_ISUPPORTS(nsAvailableMemoryWatcher, nsIObserver, nsITimerCallback)
306 :
307 : nsresult
308 : nsAvailableMemoryWatcher::Init()
309 : {
310 : mTimer = NS_NewTimer();
311 :
312 : nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
313 : MOZ_ASSERT(observerService);
314 :
315 : for (auto topic : kObserverTopics) {
316 : nsresult rv = observerService->AddObserver(this, topic,
317 : /* ownsWeak */ false);
318 : NS_ENSURE_SUCCESS(rv, rv);
319 : }
320 :
321 : MOZ_TRY(mTimer->InitWithCallback(this, kPollingIntervalMS,
322 : nsITimer::TYPE_REPEATING_SLACK));
323 : return NS_OK;
324 : }
325 :
326 : void
327 : nsAvailableMemoryWatcher::Shutdown()
328 : {
329 : nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
330 : MOZ_ASSERT(observerService);
331 :
332 : for (auto topic : kObserverTopics) {
333 : Unused << observerService->RemoveObserver(this, topic);
334 : }
335 :
336 : if (mTimer) {
337 : mTimer->Cancel();
338 : mTimer = nullptr;
339 : }
340 : }
341 :
342 : /* static */ bool
343 : nsAvailableMemoryWatcher::IsVirtualMemoryLow(const MEMORYSTATUSEX& aStat)
344 : {
345 : if ((kLowVirtualMemoryThreshold != 0) &&
346 : (aStat.ullAvailVirtual < kLowVirtualMemoryThreshold)) {
347 : sNumLowVirtualMemEvents++;
348 : return true;
349 : }
350 :
351 : return false;
352 : }
353 :
354 : /* static */ bool
355 : nsAvailableMemoryWatcher::IsCommitSpaceLow(const MEMORYSTATUSEX& aStat)
356 : {
357 : if ((kLowCommitSpaceThreshold != 0) &&
358 : (aStat.ullAvailPageFile < kLowCommitSpaceThreshold)) {
359 : sNumLowCommitSpaceEvents++;
360 : CrashReporter::AnnotateCrashReport(
361 : NS_LITERAL_CSTRING("LowCommitSpaceEvents"),
362 : nsPrintfCString("%" PRIu32, uint32_t(sNumLowCommitSpaceEvents)));
363 : return true;
364 : }
365 :
366 : return false;
367 : }
368 :
369 : /* static */ bool
370 : nsAvailableMemoryWatcher::IsPhysicalMemoryLow(const MEMORYSTATUSEX& aStat)
371 : {
372 : if ((kLowPhysicalMemoryThreshold != 0) &&
373 : (aStat.ullAvailPhys < kLowPhysicalMemoryThreshold)) {
374 : sNumLowPhysicalMemEvents++;
375 : return true;
376 : }
377 :
378 : return false;
379 : }
380 :
381 : void
382 : nsAvailableMemoryWatcher::SendMemoryPressureEvent()
383 : {
384 : MemoryPressureState state = mUnderMemoryPressure ? MemPressure_Ongoing
385 : : MemPressure_New;
386 : NS_DispatchEventualMemoryPressure(state);
387 : }
388 :
389 : void
390 : nsAvailableMemoryWatcher::AdjustPollingInterval(const bool aLowMemory)
391 : {
392 : if (aLowMemory) {
393 : // We entered a low-memory state, wait for a longer interval before polling
394 : // again as there's no point in rapidly sending further notifications.
395 : mTimer->SetDelay(kLowMemoryNotificationIntervalMS);
396 : } else if (mUnderMemoryPressure) {
397 : // We were under memory pressure but we're not anymore, resume polling at
398 : // a faster pace.
399 : mTimer->SetDelay(kPollingIntervalMS);
400 : }
401 : }
402 :
403 : // Timer callback, polls memory stats to detect low-memory conditions. This
404 : // will send memory-pressure events if memory is running low and adjust the
405 : // polling interval accordingly.
406 : NS_IMETHODIMP
407 : nsAvailableMemoryWatcher::Notify(nsITimer* aTimer)
408 : {
409 : MEMORYSTATUSEX stat;
410 : stat.dwLength = sizeof(stat);
411 : bool success = GlobalMemoryStatusEx(&stat);
412 :
413 : if (success) {
414 : bool lowMemory =
415 : IsVirtualMemoryLow(stat) ||
416 : IsCommitSpaceLow(stat) ||
417 : IsPhysicalMemoryLow(stat);
418 :
419 : if (lowMemory) {
420 : SendMemoryPressureEvent();
421 : }
422 :
423 : AdjustPollingInterval(lowMemory);
424 : mUnderMemoryPressure = lowMemory;
425 : }
426 :
427 : return NS_OK;
428 : }
429 :
430 : // Observer service callback, used to stop the polling timer when the user
431 : // stops interacting with Firefox and resuming it when they interact again.
432 : // Also used to shut down the service if the application is quitting.
433 : NS_IMETHODIMP
434 : nsAvailableMemoryWatcher::Observe(nsISupports* aSubject, const char* aTopic,
435 : const char16_t* aData)
436 : {
437 : if (strcmp(aTopic, "quit-application") == 0) {
438 : Shutdown();
439 : } else if (strcmp(aTopic, "user-interaction-inactive") == 0) {
440 : mTimer->Cancel();
441 : } else if (strcmp(aTopic, "user-interaction-active") == 0) {
442 : mTimer->InitWithCallback(this, kPollingIntervalMS,
443 : nsITimer::TYPE_REPEATING_SLACK);
444 : } else {
445 : MOZ_ASSERT_UNREACHABLE("Unknown topic");
446 : }
447 :
448 : return NS_OK;
449 : }
450 :
451 : #endif // !defined(HAVE_64BIT_BUILD)
452 :
453 : static int64_t
454 : LowMemoryEventsVirtualDistinguishedAmount()
455 : {
456 : return sNumLowVirtualMemEvents;
457 : }
458 :
459 : static int64_t
460 : LowMemoryEventsCommitSpaceDistinguishedAmount()
461 : {
462 : return sNumLowCommitSpaceEvents;
463 : }
464 :
465 : static int64_t
466 : LowMemoryEventsPhysicalDistinguishedAmount()
467 : {
468 : return sNumLowPhysicalMemEvents;
469 : }
470 :
471 : class LowEventsReporter final : public nsIMemoryReporter
472 : {
473 : ~LowEventsReporter() {}
474 :
475 : public:
476 : NS_DECL_ISUPPORTS
477 :
478 : NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
479 : nsISupports* aData, bool aAnonymize) override
480 : {
481 : MOZ_COLLECT_REPORT(
482 : "low-memory-events/virtual", KIND_OTHER, UNITS_COUNT_CUMULATIVE,
483 : LowMemoryEventsVirtualDistinguishedAmount(),
484 : "Number of low-virtual-memory events fired since startup. We fire such an "
485 : "event if we notice there is less than memory.low_virtual_mem_threshold_mb of "
486 : "virtual address space available (if zero, this behavior is disabled). The "
487 : "process will probably crash if it runs out of virtual address space, so "
488 : "this event is dire.");
489 :
490 : MOZ_COLLECT_REPORT(
491 : "low-memory-events/commit-space", KIND_OTHER, UNITS_COUNT_CUMULATIVE,
492 : LowMemoryEventsCommitSpaceDistinguishedAmount(),
493 : "Number of low-commit-space events fired since startup. We fire such an "
494 : "event if we notice there is less than memory.low_commit_space_threshold_mb of "
495 : "commit space available (if zero, this behavior is disabled). Windows will "
496 : "likely kill the process if it runs out of commit space, so this event is "
497 : "dire.");
498 :
499 : MOZ_COLLECT_REPORT(
500 : "low-memory-events/physical", KIND_OTHER, UNITS_COUNT_CUMULATIVE,
501 : LowMemoryEventsPhysicalDistinguishedAmount(),
502 : "Number of low-physical-memory events fired since startup. We fire such an "
503 : "event if we notice there is less than memory.low_physical_memory_threshold_mb "
504 : "of physical memory available (if zero, this behavior is disabled). The "
505 : "machine will start to page if it runs out of physical memory. This may "
506 : "cause it to run slowly, but it shouldn't cause it to crash.");
507 :
508 : return NS_OK;
509 : }
510 : };
511 : NS_IMPL_ISUPPORTS(LowEventsReporter, nsIMemoryReporter)
512 :
513 : #endif // defined(XP_WIN)
514 :
515 : /**
516 : * This runnable is executed in response to a memory-pressure event; we spin
517 : * the event-loop when receiving the memory-pressure event in the hope that
518 : * other observers will synchronously free some memory that we'll be able to
519 : * purge here.
520 : */
521 0 : class nsJemallocFreeDirtyPagesRunnable final : public nsIRunnable
522 : {
523 0 : ~nsJemallocFreeDirtyPagesRunnable() {}
524 :
525 : public:
526 : NS_DECL_ISUPPORTS
527 : NS_DECL_NSIRUNNABLE
528 : };
529 :
530 0 : NS_IMPL_ISUPPORTS(nsJemallocFreeDirtyPagesRunnable, nsIRunnable)
531 :
532 : NS_IMETHODIMP
533 0 : nsJemallocFreeDirtyPagesRunnable::Run()
534 : {
535 0 : MOZ_ASSERT(NS_IsMainThread());
536 :
537 : #if defined(MOZ_MEMORY)
538 0 : jemalloc_free_dirty_pages();
539 : #endif
540 :
541 0 : return NS_OK;
542 : }
543 :
544 : /**
545 : * The memory pressure watcher is used for listening to memory-pressure events
546 : * and reacting upon them. We use one instance per process currently only for
547 : * cleaning up dirty unused pages held by jemalloc.
548 : */
549 3 : class nsMemoryPressureWatcher final : public nsIObserver
550 : {
551 0 : ~nsMemoryPressureWatcher() {}
552 :
553 : public:
554 : NS_DECL_ISUPPORTS
555 : NS_DECL_NSIOBSERVER
556 :
557 : void Init();
558 : };
559 :
560 12 : NS_IMPL_ISUPPORTS(nsMemoryPressureWatcher, nsIObserver)
561 :
562 : /**
563 : * Initialize and subscribe to the memory-pressure events. We subscribe to the
564 : * observer service in this method and not in the constructor because we need
565 : * to hold a strong reference to 'this' before calling the observer service.
566 : */
567 : void
568 0 : nsMemoryPressureWatcher::Init()
569 : {
570 0 : nsCOMPtr<nsIObserverService> os = services::GetObserverService();
571 :
572 1 : if (os) {
573 1 : os->AddObserver(this, "memory-pressure", /* ownsWeak */ false);
574 : }
575 1 : }
576 :
577 : /**
578 : * Reacts to all types of memory-pressure events, launches a runnable to
579 : * free dirty pages held by jemalloc.
580 : */
581 : NS_IMETHODIMP
582 0 : nsMemoryPressureWatcher::Observe(nsISupports* aSubject, const char* aTopic,
583 : const char16_t* aData)
584 : {
585 0 : MOZ_ASSERT(!strcmp(aTopic, "memory-pressure"), "Unknown topic");
586 :
587 0 : nsCOMPtr<nsIRunnable> runnable = new nsJemallocFreeDirtyPagesRunnable();
588 :
589 0 : NS_DispatchToMainThread(runnable);
590 :
591 0 : return NS_OK;
592 : }
593 :
594 : } // namespace
595 :
596 : namespace mozilla {
597 : namespace AvailableMemoryTracker {
598 :
599 : void
600 1 : Activate()
601 : {
602 : #if defined(XP_WIN) && !defined(HAVE_64BIT_BUILD)
603 : MOZ_ASSERT(sInitialized);
604 : MOZ_ASSERT(!sHooksActive);
605 :
606 : RegisterStrongMemoryReporter(new LowEventsReporter());
607 : RegisterLowMemoryEventsVirtualDistinguishedAmount(
608 : LowMemoryEventsVirtualDistinguishedAmount);
609 : RegisterLowMemoryEventsPhysicalDistinguishedAmount(
610 : LowMemoryEventsPhysicalDistinguishedAmount);
611 : sHooksActive = true;
612 : #endif // defined(XP_WIN) && !defined(HAVE_64BIT_BUILD)
613 :
614 : // The watchers are held alive by the observer service.
615 2 : RefPtr<nsMemoryPressureWatcher> watcher = new nsMemoryPressureWatcher();
616 1 : watcher->Init();
617 :
618 : #if defined(XP_WIN) && defined(HAVE_64BIT_BUILD)
619 : if (XRE_IsParentProcess()) {
620 : RefPtr<nsAvailableMemoryWatcher> poller = new nsAvailableMemoryWatcher();
621 :
622 : if (NS_FAILED(poller->Init())) {
623 : NS_WARNING("Could not start the available memory watcher");
624 : }
625 : }
626 : #endif // defined(XP_WIN) && defined(HAVE_64BIT_BUILD)
627 1 : }
628 :
629 : void
630 1 : Init()
631 : {
632 : // Do nothing on x86-64, because nsWindowsDllInterceptor is not thread-safe
633 : // on 64-bit. (On 32-bit, it's probably thread-safe.) Even if we run Init()
634 : // before any other of our threads are running, another process may have
635 : // started a remote thread which could call VirtualAlloc!
636 : //
637 : // Moreover, the benefit of this code is less clear when we're a 64-bit
638 : // process, because we aren't going to run out of virtual memory, and the
639 : // system is likely to have a fair bit of physical memory.
640 :
641 : #if defined(XP_WIN) && !defined(HAVE_64BIT_BUILD)
642 : // Don't register the hooks if we're a build instrumented for PGO: If we're
643 : // an instrumented build, the compiler adds function calls all over the place
644 : // which may call VirtualAlloc; this makes it hard to prevent
645 : // VirtualAllocHook from reentering itself.
646 : if (!PR_GetEnv("MOZ_PGO_INSTRUMENTED")) {
647 : sKernel32Intercept.Init("Kernel32.dll");
648 : sKernel32Intercept.AddHook("VirtualAlloc",
649 : reinterpret_cast<intptr_t>(VirtualAllocHook),
650 : reinterpret_cast<void**>(&sVirtualAllocOrig));
651 : sKernel32Intercept.AddHook("MapViewOfFile",
652 : reinterpret_cast<intptr_t>(MapViewOfFileHook),
653 : reinterpret_cast<void**>(&sMapViewOfFileOrig));
654 :
655 : sGdi32Intercept.Init("Gdi32.dll");
656 : sGdi32Intercept.AddHook("CreateDIBSection",
657 : reinterpret_cast<intptr_t>(CreateDIBSectionHook),
658 : reinterpret_cast<void**>(&sCreateDIBSectionOrig));
659 : }
660 :
661 : sInitialized = true;
662 : #endif // defined(XP_WIN) && !defined(HAVE_64BIT_BUILD)
663 : }
664 :
665 : } // namespace AvailableMemoryTracker
666 : } // namespace mozilla
|