Skip to content

Add memory usage to contexts #2133

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

- Capture total frames, frames delay, slow & frozen frames and attach to spans ([#2106](https://github.com/getsentry/sentry-dart/pull/2106))
- Support WebAssembly compilation (dart2wasm) ([#2113](https://github.com/getsentry/sentry-dart/pull/2113))
- Add memory usage to contexts ([#2133](https://github.com/getsentry/sentry-dart/pull/2133))
- Only for Linux/Windows applications, as iOS/Android use native SDKs
- No web support, more info: https://developer.mozilla.org/en-US/docs/Web/API/Performance/memory#usedjsheapsize

### Deprecated

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:io';

import '../../../sentry.dart';
import 'enricher_event_processor.dart';
import 'io_platform_memory.dart';

EnricherEventProcessor enricherEventProcessor(SentryOptions options) {
return IoEnricherEventProcessor(options);
Expand All @@ -20,22 +21,27 @@ class IoEnricherEventProcessor implements EnricherEventProcessor {
// If there's a native integration available, it probably has better
// information available than Flutter.

final device = _options.platformChecker.hasNativeIntegration
? null
: _getDevice(event.contexts.device);

final os = _options.platformChecker.hasNativeIntegration
? null
: _getOperatingSystem(event.contexts.operatingSystem);

final device = _options.platformChecker.hasNativeIntegration
final app = _options.platformChecker.hasNativeIntegration
? null
: _getDevice(event.contexts.device);
: _getApp(event.contexts.app);

final culture = _options.platformChecker.hasNativeIntegration
? null
: _getSentryCulture(event.contexts.culture);

final contexts = event.contexts.copyWith(
operatingSystem: os,
device: device,
operatingSystem: os,
runtimes: _getRuntimes(event.contexts.runtimes),
app: app,
culture: culture,
);

Expand Down Expand Up @@ -97,9 +103,18 @@ class IoEnricherEventProcessor implements EnricherEventProcessor {
}

SentryDevice _getDevice(SentryDevice? device) {
final platformMemory = PlatformMemory(Platform.operatingSystem);
return (device ?? SentryDevice()).copyWith(
name: device?.name ?? Platform.localHostname,
processorCount: device?.processorCount ?? Platform.numberOfProcessors,
memorySize: platformMemory.getTotalPhysicalMemory() ?? device?.memorySize,
freeMemory: platformMemory.getFreePhysicalMemory() ?? device?.freeMemory,
);
}

SentryApp _getApp(SentryApp? app) {
return (app ?? SentryApp()).copyWith(
appMemory: ProcessInfo.currentRss,
);
}

Expand Down
107 changes: 107 additions & 0 deletions dart/lib/src/event_processor/enricher/io_platform_memory.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import 'dart:io';

// Get total & free platform memory (in bytes) for linux and windows operating systems.
// Source: https://github.com/onepub-dev/system_info/blob/8a9bf6b8eb7c86a09b3c3df4bf6d7fa5a6b50732/lib/src/platform/memory.dart
class PlatformMemory {
PlatformMemory(this.operatingSystem);

final String operatingSystem;

int? getTotalPhysicalMemory() {
switch (operatingSystem) {
case 'linux':
return _getLinuxMemInfoValue('MemTotal');
case 'windows':
return _getWindowsWmicValue('ComputerSystem', 'TotalPhysicalMemory');

Check warning on line 15 in dart/lib/src/event_processor/enricher/io_platform_memory.dart

View check run for this annotation

Codecov / codecov/patch

dart/lib/src/event_processor/enricher/io_platform_memory.dart#L14-L15

Added lines #L14 - L15 were not covered by tests
default:
return null;
}
}

int? getFreePhysicalMemory() {
switch (operatingSystem) {
case 'linux':
return _getLinuxMemInfoValue('MemFree');
case 'windows':
return _getWindowsWmicValue('OS', 'FreePhysicalMemory');

Check warning on line 26 in dart/lib/src/event_processor/enricher/io_platform_memory.dart

View check run for this annotation

Codecov / codecov/patch

dart/lib/src/event_processor/enricher/io_platform_memory.dart#L25-L26

Added lines #L25 - L26 were not covered by tests
default:
return null;
}
}

int? _getWindowsWmicValue(String section, String key) {
final os = _wmicGetValueAsMap(section, [key]);
final totalPhysicalMemoryValue = os?[key];

Check warning on line 34 in dart/lib/src/event_processor/enricher/io_platform_memory.dart

View check run for this annotation

Codecov / codecov/patch

dart/lib/src/event_processor/enricher/io_platform_memory.dart#L32-L34

Added lines #L32 - L34 were not covered by tests
if (totalPhysicalMemoryValue == null) {
return null;
}
final size = int.tryParse(totalPhysicalMemoryValue);

Check warning on line 38 in dart/lib/src/event_processor/enricher/io_platform_memory.dart

View check run for this annotation

Codecov / codecov/patch

dart/lib/src/event_processor/enricher/io_platform_memory.dart#L38

Added line #L38 was not covered by tests
if (size == null) {
return null;
}
return size;
}

int? _getLinuxMemInfoValue(String key) {
final meminfoList = _exec('cat', ['/proc/meminfo'])
?.trim()
.replaceAll('\r\n', '\n')
.split('\n') ??
[];

Check warning on line 50 in dart/lib/src/event_processor/enricher/io_platform_memory.dart

View check run for this annotation

Codecov / codecov/patch

dart/lib/src/event_processor/enricher/io_platform_memory.dart#L50

Added line #L50 was not covered by tests

final meminfoMap = _listToMap(meminfoList, ':');
final memsizeResults = meminfoMap[key]?.split(' ') ?? [];

if (memsizeResults.isEmpty) {
return null;
}
final memsizeResult = memsizeResults.first;

final memsize = int.tryParse(memsizeResult);
if (memsize == null) {
return null;
}
return memsize;
}

String? _exec(String executable, List<String> arguments,
{bool runInShell = false}) {
try {
final result =
Process.runSync(executable, arguments, runInShell: runInShell);
if (result.exitCode == 0) {
return result.stdout.toString();
}
} catch (e) {
//
}
return null;
}

Map<String, String>? _wmicGetValueAsMap(String section, List<String> fields) {
final arguments = <String>[section];

Check warning on line 82 in dart/lib/src/event_processor/enricher/io_platform_memory.dart

View check run for this annotation

Codecov / codecov/patch

dart/lib/src/event_processor/enricher/io_platform_memory.dart#L81-L82

Added lines #L81 - L82 were not covered by tests
arguments
..add('get')
..addAll(fields.join(', ').split(' '))
..add('/VALUE');

Check warning on line 86 in dart/lib/src/event_processor/enricher/io_platform_memory.dart

View check run for this annotation

Codecov / codecov/patch

dart/lib/src/event_processor/enricher/io_platform_memory.dart#L84-L86

Added lines #L84 - L86 were not covered by tests

final list =
_exec('wmic', arguments)?.trim().replaceAll('\r\n', '\n').split('\n') ??
[];

Check warning on line 90 in dart/lib/src/event_processor/enricher/io_platform_memory.dart

View check run for this annotation

Codecov / codecov/patch

dart/lib/src/event_processor/enricher/io_platform_memory.dart#L89-L90

Added lines #L89 - L90 were not covered by tests

return _listToMap(list, '=');

Check warning on line 92 in dart/lib/src/event_processor/enricher/io_platform_memory.dart

View check run for this annotation

Codecov / codecov/patch

dart/lib/src/event_processor/enricher/io_platform_memory.dart#L92

Added line #L92 was not covered by tests
}

Map<String, String> _listToMap(List<String> list, String separator) {
final map = <String, String>{};
for (final string in list) {
final index = string.indexOf(separator);
if (index != -1) {
final key = string.substring(0, index).trim();
final value = string.substring(index + 1).trim();
map[key] = value;
}
}
return map;
}
}
57 changes: 57 additions & 0 deletions dart/test/event_processor/enricher/io_platform_memory_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
@TestOn('vm')
library dart_test;

import 'dart:io';

import 'package:sentry/src/event_processor/enricher/io_platform_memory.dart';
import 'package:test/test.dart';

void main() {
late Fixture fixture;

setUp(() {
fixture = Fixture();
});

test('total physical memory', () {
final sut = fixture.getSut();
final totalPhysicalMemory = sut.getTotalPhysicalMemory();

switch (Platform.operatingSystem) {
case 'linux':
expect(totalPhysicalMemory, isNotNull);
expect(totalPhysicalMemory! > 0, true);
break;
case 'windows':
expect(totalPhysicalMemory, isNotNull);
expect(totalPhysicalMemory! > 0, true);
break;
default:
expect(totalPhysicalMemory, isNull);
}
});

test('free physical memory', () {
final sut = fixture.getSut();
final freePhysicalMemory = sut.getTotalPhysicalMemory();

switch (Platform.operatingSystem) {
case 'linux':
expect(freePhysicalMemory, isNotNull);
expect(freePhysicalMemory! > 0, true);
break;
case 'windows':
expect(freePhysicalMemory, isNotNull);
expect(freePhysicalMemory! > 0, true);
break;
default:
expect(freePhysicalMemory, isNull);
}
});
}

class Fixture {
PlatformMemory getSut() {
return PlatformMemory(Platform.operatingSystem);
}
}
Loading