2021-07-27 19:17:43 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
#include <sys/sysctl.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <time.h>
|
|
|
|
|
|
|
|
#include "AvailableMemoryWatcher.h"
|
|
|
|
#include "Logging.h"
|
|
|
|
#include "nsExceptionHandler.h"
|
|
|
|
#include "nsICrashReporter.h"
|
|
|
|
#include "nsISupports.h"
|
|
|
|
#include "nsMemoryPressure.h"
|
|
|
|
|
|
|
|
#define MP_LOG(...) MOZ_LOG(gMPLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
|
|
|
|
static mozilla::LazyLogModule gMPLog("MemoryPressure");
|
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
|
|
|
|
class nsAvailableMemoryWatcher final : public nsAvailableMemoryWatcherBase {
|
|
|
|
public:
|
|
|
|
NS_DECL_ISUPPORTS_INHERITED
|
|
|
|
|
|
|
|
nsAvailableMemoryWatcher();
|
|
|
|
nsresult Init();
|
|
|
|
|
|
|
|
void OnMemoryPressureChanged(MacMemoryPressureLevel aLevel) override;
|
|
|
|
void AddChildAnnotations(
|
|
|
|
const UniquePtr<ipc::CrashReporterHost>& aCrashReporter) override;
|
|
|
|
|
|
|
|
private:
|
|
|
|
~nsAvailableMemoryWatcher(){};
|
|
|
|
|
|
|
|
void InitParentAnnotations();
|
|
|
|
void UpdateParentAnnotations();
|
|
|
|
|
|
|
|
void AddParentAnnotation(CrashReporter::Annotation aAnnotation,
|
|
|
|
nsAutoCString aString) {
|
|
|
|
CrashReporter::AnnotateCrashReport(aAnnotation, aString);
|
|
|
|
}
|
|
|
|
void AddParentAnnotation(CrashReporter::Annotation aAnnotation,
|
|
|
|
uint32_t aData) {
|
|
|
|
CrashReporter::AnnotateCrashReport(aAnnotation, aData);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ReadSysctls();
|
|
|
|
|
|
|
|
// Init has been called.
|
|
|
|
bool mInitialized;
|
|
|
|
|
|
|
|
// The memory pressure reported to the application by macOS.
|
|
|
|
MacMemoryPressureLevel mLevel;
|
|
|
|
|
|
|
|
// The value of the vm.memory_pressure sysctl. The OS notifies the
|
|
|
|
// application when the memory pressure level changes, but the sysctl
|
|
|
|
// value can be read at any time. Unofficially, the sysctl value
|
|
|
|
// corresponds to the OS memory pressure level with 4=>critical,
|
|
|
|
// 2=>warning, and 1=>normal.
|
|
|
|
uint32_t mLevelSysctl;
|
|
|
|
|
|
|
|
// The value of the kern.memorystatus_level sysctl. Unofficially,
|
|
|
|
// this is the percentage of available memory. (Also readable
|
|
|
|
// via the undocumented memorystatus_get_level syscall.)
|
|
|
|
int mAvailMemSysctl;
|
|
|
|
|
|
|
|
// The string representation of `mLevel`. i.e., normal, warning, or critical.
|
|
|
|
// Set to "unset" until a memory pressure change is reported to the process
|
|
|
|
// by the OS.
|
|
|
|
nsAutoCString mLevelStr;
|
|
|
|
|
|
|
|
// Timestamps for memory pressure level changes. Specifically, the Unix
|
|
|
|
// time in string form. Saved as Unix time to allow comparisons with
|
|
|
|
// the crash time.
|
|
|
|
nsAutoCString mNormalTimeStr;
|
|
|
|
nsAutoCString mWarningTimeStr;
|
|
|
|
nsAutoCString mCriticalTimeStr;
|
|
|
|
};
|
|
|
|
|
|
|
|
NS_IMPL_ISUPPORTS(nsAvailableMemoryWatcher, nsIAvailableMemoryWatcherBase);
|
|
|
|
|
|
|
|
nsAvailableMemoryWatcher::nsAvailableMemoryWatcher()
|
|
|
|
: mInitialized(false),
|
2021-08-05 09:07:39 +03:00
|
|
|
mLevel(MacMemoryPressureLevel::Value::eUnset),
|
2021-07-27 19:17:43 +03:00
|
|
|
mLevelSysctl(0xFFFFFFFF),
|
|
|
|
mAvailMemSysctl(-1),
|
|
|
|
mLevelStr("Unset"),
|
|
|
|
mNormalTimeStr("Unset"),
|
|
|
|
mWarningTimeStr("Unset"),
|
|
|
|
mCriticalTimeStr("Unset") {}
|
|
|
|
|
|
|
|
nsresult nsAvailableMemoryWatcher::Init() {
|
|
|
|
// Users of nsAvailableMemoryWatcher should use
|
|
|
|
// nsAvailableMemoryWatcherBase::GetSingleton() and not call Init directly.
|
|
|
|
MOZ_ASSERT(!mInitialized);
|
|
|
|
if (mInitialized) {
|
|
|
|
return NS_ERROR_ALREADY_INITIALIZED;
|
|
|
|
}
|
|
|
|
|
|
|
|
ReadSysctls();
|
|
|
|
MP_LOG("Initial memory pressure sysctl: %d", mLevelSysctl);
|
|
|
|
MP_LOG("Initial available memory sysctl: %d", mAvailMemSysctl);
|
|
|
|
|
|
|
|
// Set the initial state of all annotations for parent crash reports.
|
|
|
|
// Content process crash reports are set when a crash occurs and
|
|
|
|
// AddChildAnnotations() is called.
|
|
|
|
CrashReporter::AnnotateCrashReport(
|
|
|
|
CrashReporter::Annotation::MacMemoryPressure, mLevelStr);
|
|
|
|
CrashReporter::AnnotateCrashReport(
|
|
|
|
CrashReporter::Annotation::MacMemoryPressureNormalTime, mNormalTimeStr);
|
|
|
|
CrashReporter::AnnotateCrashReport(
|
|
|
|
CrashReporter::Annotation::MacMemoryPressureWarningTime, mWarningTimeStr);
|
|
|
|
CrashReporter::AnnotateCrashReport(
|
|
|
|
CrashReporter::Annotation::MacMemoryPressureCriticalTime,
|
|
|
|
mCriticalTimeStr);
|
|
|
|
CrashReporter::AnnotateCrashReport(
|
|
|
|
CrashReporter::Annotation::MacMemoryPressureSysctl, mLevelSysctl);
|
|
|
|
CrashReporter::AnnotateCrashReport(
|
|
|
|
CrashReporter::Annotation::MacAvailableMemorySysctl, mAvailMemSysctl);
|
|
|
|
|
|
|
|
mInitialized = true;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<nsAvailableMemoryWatcherBase> CreateAvailableMemoryWatcher() {
|
|
|
|
// Users of nsAvailableMemoryWatcher should use
|
|
|
|
// nsAvailableMemoryWatcherBase::GetSingleton().
|
|
|
|
RefPtr watcher(new nsAvailableMemoryWatcher());
|
|
|
|
watcher->Init();
|
|
|
|
return watcher.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the memory pressure level, level change timestamps, and sysctl
|
|
|
|
// level crash report annotations.
|
|
|
|
void nsAvailableMemoryWatcher::UpdateParentAnnotations() {
|
|
|
|
// Generate a string representation of the current Unix time.
|
|
|
|
time_t timeChanged = time(NULL);
|
|
|
|
nsAutoCString timeChangedString;
|
|
|
|
timeChangedString =
|
|
|
|
nsPrintfCString("%" PRIu64, static_cast<uint64_t>(timeChanged));
|
|
|
|
|
|
|
|
nsAutoCString pressureLevelString;
|
|
|
|
Maybe<CrashReporter::Annotation> pressureLevelKey;
|
|
|
|
|
2021-08-05 09:07:39 +03:00
|
|
|
switch (mLevel.GetValue()) {
|
|
|
|
case MacMemoryPressureLevel::Value::eNormal:
|
2021-07-27 19:17:43 +03:00
|
|
|
mNormalTimeStr = timeChangedString;
|
|
|
|
pressureLevelString = "Normal";
|
|
|
|
pressureLevelKey.emplace(
|
|
|
|
CrashReporter::Annotation::MacMemoryPressureNormalTime);
|
|
|
|
break;
|
2021-08-05 09:07:39 +03:00
|
|
|
case MacMemoryPressureLevel::Value::eWarning:
|
2021-07-27 19:17:43 +03:00
|
|
|
mWarningTimeStr = timeChangedString;
|
|
|
|
pressureLevelString = "Warning";
|
|
|
|
pressureLevelKey.emplace(
|
|
|
|
CrashReporter::Annotation::MacMemoryPressureWarningTime);
|
|
|
|
break;
|
2021-08-05 09:07:39 +03:00
|
|
|
case MacMemoryPressureLevel::Value::eCritical:
|
2021-07-27 19:17:43 +03:00
|
|
|
mCriticalTimeStr = timeChangedString;
|
|
|
|
pressureLevelString = "Critical";
|
|
|
|
pressureLevelKey.emplace(
|
|
|
|
CrashReporter::Annotation::MacMemoryPressureCriticalTime);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
pressureLevelString = "Unexpected";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
MP_LOG("Transitioning to %s at time %s", pressureLevelString.get(),
|
|
|
|
timeChangedString.get());
|
|
|
|
|
|
|
|
// Save the current memory pressure level.
|
|
|
|
AddParentAnnotation(CrashReporter::Annotation::MacMemoryPressure,
|
|
|
|
pressureLevelString);
|
|
|
|
|
|
|
|
// Save the time we transitioned to the current memory pressure level.
|
|
|
|
if (pressureLevelKey.isSome()) {
|
|
|
|
AddParentAnnotation(pressureLevelKey.value(), timeChangedString);
|
|
|
|
}
|
|
|
|
|
|
|
|
AddParentAnnotation(CrashReporter::Annotation::MacMemoryPressureSysctl,
|
|
|
|
mLevelSysctl);
|
|
|
|
AddParentAnnotation(CrashReporter::Annotation::MacAvailableMemorySysctl,
|
|
|
|
mAvailMemSysctl);
|
|
|
|
}
|
|
|
|
|
|
|
|
void nsAvailableMemoryWatcher::ReadSysctls() {
|
|
|
|
// Pressure level
|
|
|
|
uint32_t level;
|
|
|
|
size_t size = sizeof(level);
|
|
|
|
if (sysctlbyname("kern.memorystatus_vm_pressure_level", &level, &size, NULL,
|
|
|
|
0) == -1) {
|
|
|
|
MP_LOG("Failure reading memory pressure sysctl");
|
|
|
|
}
|
|
|
|
mLevelSysctl = level;
|
|
|
|
|
|
|
|
// Available memory percent
|
|
|
|
int availPercent;
|
|
|
|
size = sizeof(availPercent);
|
2021-08-05 09:07:39 +03:00
|
|
|
if (sysctlbyname("kern.memorystatus_level", &availPercent, &size, NULL, 0) ==
|
|
|
|
-1) {
|
2021-07-27 19:17:43 +03:00
|
|
|
MP_LOG("Failure reading available memory level");
|
|
|
|
}
|
|
|
|
mAvailMemSysctl = availPercent;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* virtual */
|
|
|
|
void nsAvailableMemoryWatcher::OnMemoryPressureChanged(
|
|
|
|
MacMemoryPressureLevel aLevel) {
|
|
|
|
MOZ_ASSERT(mInitialized);
|
|
|
|
mLevel = aLevel;
|
|
|
|
ReadSysctls();
|
|
|
|
MP_LOG("level: %s, level sysctl: %d, available memory: %d percent",
|
2021-08-05 09:07:39 +03:00
|
|
|
aLevel.ToString(), mLevelSysctl, mAvailMemSysctl);
|
2021-07-27 19:17:43 +03:00
|
|
|
UpdateParentAnnotations();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* virtual */
|
|
|
|
// Add all annotations to the provided crash reporter instance.
|
|
|
|
void nsAvailableMemoryWatcher::AddChildAnnotations(
|
|
|
|
const UniquePtr<ipc::CrashReporterHost>& aCrashReporter) {
|
|
|
|
aCrashReporter->AddAnnotation(CrashReporter::Annotation::MacMemoryPressure,
|
|
|
|
mLevelStr);
|
|
|
|
aCrashReporter->AddAnnotation(
|
|
|
|
CrashReporter::Annotation::MacMemoryPressureNormalTime, mNormalTimeStr);
|
|
|
|
aCrashReporter->AddAnnotation(
|
|
|
|
CrashReporter::Annotation::MacMemoryPressureWarningTime, mWarningTimeStr);
|
|
|
|
aCrashReporter->AddAnnotation(
|
|
|
|
CrashReporter::Annotation::MacMemoryPressureCriticalTime,
|
|
|
|
mCriticalTimeStr);
|
|
|
|
aCrashReporter->AddAnnotation(
|
|
|
|
CrashReporter::Annotation::MacMemoryPressureSysctl, mLevelSysctl);
|
|
|
|
aCrashReporter->AddAnnotation(
|
|
|
|
CrashReporter::Annotation::MacAvailableMemorySysctl, mAvailMemSysctl);
|
|
|
|
}
|
|
|
|
} // namespace mozilla
|