Page MenuHomePhabricator

D11863.id28576.diff
No OneTemporary

D11863.id28576.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -317,6 +317,8 @@
'PhutilSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilSyntaxHighlighter.php',
'PhutilSyntaxHighlighterEngine' => 'markup/syntax/engine/PhutilSyntaxHighlighterEngine.php',
'PhutilSyntaxHighlighterException' => 'markup/syntax/highlighter/PhutilSyntaxHighlighterException.php',
+ 'PhutilSystem' => 'utils/PhutilSystem.php',
+ 'PhutilSystemTestCase' => 'utils/__tests__/PhutilSystemTestCase.php',
'PhutilTestCase' => 'infrastructure/testing/PhutilTestCase.php',
'PhutilTestPhobject' => 'object/__tests__/PhutilTestPhobject.php',
'PhutilTortureTestDaemon' => 'daemon/torture/PhutilTortureTestDaemon.php',
@@ -715,6 +717,8 @@
'PhutilSimpleOptionsTestCase' => 'PhutilTestCase',
'PhutilSocketChannel' => 'PhutilChannel',
'PhutilSyntaxHighlighterException' => 'Exception',
+ 'PhutilSystem' => 'Phobject',
+ 'PhutilSystemTestCase' => 'PhutilTestCase',
'PhutilTestCase' => 'ArcanistPhutilTestCase',
'PhutilTestPhobject' => 'Phobject',
'PhutilTortureTestDaemon' => 'PhutilDaemon',
diff --git a/src/utils/PhutilSystem.php b/src/utils/PhutilSystem.php
new file mode 100644
--- /dev/null
+++ b/src/utils/PhutilSystem.php
@@ -0,0 +1,158 @@
+<?php
+
+/**
+ * Interact with the operating system.
+ *
+ * @task memory Interacting with System Memory
+ */
+final class PhutilSystem extends Phobject {
+
+
+ /**
+ * Get information about total and free memory on the system.
+ *
+ * Because "free memory" is a murky concept, the interpretation of the values
+ * returned from this method will vary from system to system and the numbers
+ * themselves may be only roughly accurate.
+ *
+ * @return map<string, wild> Dictionary of memory information.
+ * @task memory
+ */
+ public static function getSystemMemoryInformation() {
+ $meminfo_path = '/proc/meminfo';
+ if (Filesystem::pathExists($meminfo_path)) {
+ $meminfo_data = Filesystem::readFile($meminfo_path);
+ return self::parseMemInfo($meminfo_data);
+ } else if (Filesystem::binaryExists('vm_stat')) {
+ list($vm_stat_stdout) = execx('vm_stat');
+ return self::parseVMStat($vm_stat_stdout);
+ } else {
+ throw new Exception(
+ pht(
+ 'Unable to access /proc/meminfo or `vm_stat` on this system to '.
+ 'get system memory information.'));
+ }
+ }
+
+
+ /**
+ * Parse the output of `/proc/meminfo`.
+ *
+ * See @{method:getSystemMemoryInformation}. This method is used to get memory
+ * information on Linux.
+ *
+ * @param string Raw `/proc/meminfo`.
+ * @return map<string, wild> Parsed memory information.
+ * @task memory
+ */
+ public static function parseMemInfo($data) {
+ $data = phutil_split_lines($data);
+
+ $map = array();
+ foreach ($data as $line) {
+ list($key, $value) = explode(':', $line, 2);
+ $key = trim($key);
+ $value = trim($value);
+
+ $matches = null;
+ if (preg_match('/^(\d+) kB\z/', $value, $matches)) {
+ $value = (int)$matches[1] * 1024;
+ }
+
+ $map[$key] = $value;
+ }
+
+ $expect = array(
+ 'MemTotal',
+ 'MemFree',
+ 'Buffers',
+ 'Cached',
+ );
+ foreach ($expect as $key) {
+ if (!array_key_exists($key, $map)) {
+ throw new Exception(
+ pht(
+ 'Expected to find "%s" in "/proc/meminfo" output, but did not.',
+ $key));
+ }
+ }
+
+ $total = $map['MemTotal'];
+ $free = $map['MemFree'] + $map['Buffers'] + $map['Cached'];
+
+ return array(
+ 'total' => $total,
+ 'free' => $free,
+ );
+ }
+
+
+ /**
+ * Parse the output of `vm_stat`.
+ *
+ * See @{method:getSystemMemoryInformation}. This method is used to get memory
+ * information on Mac OS X.
+ *
+ * @param string Raw `vm_stat` output.
+ * @return map<string, wild> Parsed memory information.
+ * @task memory
+ */
+ public static function parseVMStat($data) {
+ $data = phutil_split_lines($data);
+
+ $page_size = null;
+ $map = array();
+
+ foreach ($data as $line) {
+ list($key, $value) = explode(':', $line, 2);
+ $key = trim($key);
+ $value = trim($value);
+
+ $matches = null;
+ if (preg_match('/page size of (\d+) bytes/', $value, $matches)) {
+ $page_size = (int)$matches[1];
+ continue;
+ }
+
+ $value = trim($value, '.');
+ $map[$key] = $value;
+ }
+
+ if (!$page_size) {
+ throw new Exception(
+ pht(
+ 'Expected to find "page size" in `vm_stat` output, but did not.'));
+ }
+
+ $expect = array(
+ 'Pages free',
+ 'Pages active',
+ 'Pages inactive',
+ 'Pages wired down',
+ );
+ foreach ($expect as $key) {
+ if (!array_key_exists($key, $map)) {
+ throw new Exception(
+ pht(
+ 'Expected to find "%s" in `vm_stat` output, but did not.',
+ $key));
+ }
+ }
+
+ // NOTE: This calculation probably isn't quite right. In particular,
+ // the numbers don't exactly add up, and "Pages inactive" includes a
+ // bunch of disk cache. So these numbers aren't totally reliable and they
+ // aren't directly comparable to the /proc/meminfo numbers.
+
+ $free = $map['Pages free'];
+ $active = $map['Pages active'];
+ $inactive = $map['Pages inactive'];
+ $wired = $map['Pages wired down'];
+
+ return array(
+ 'total' => ($free + $active + $inactive + $wired) * $page_size,
+ 'free' => ($free) * $page_size,
+ );
+ }
+
+}
diff --git a/src/utils/__tests__/PhutilSystemTestCase.php b/src/utils/__tests__/PhutilSystemTestCase.php
new file mode 100644
--- /dev/null
+++ b/src/utils/__tests__/PhutilSystemTestCase.php
@@ -0,0 +1,43 @@
+<?php
+
+final class PhutilSystemTestCase extends PhutilTestCase {
+
+ public function testParseVMStat() {
+ $tests = array(
+ 'vmstat.yosemite.txt' => array(
+ 'total' => 16503578624,
+ 'free' => 1732366336,
+ ),
+ );
+
+ $dir = dirname(__FILE__).'/memory';
+ foreach ($tests as $input => $expect) {
+ $raw = Filesystem::readFile($dir.'/'.$input);
+ $actual = PhutilSystem::parseVMStat($raw);
+ $this->assertEqual(
+ $expect,
+ $actual,
+ pht('Parse of "%s".', $input));
+ }
+ }
+
+ public function testParseMeminfo() {
+ $tests = array(
+ 'meminfo.ubuntu14.txt' => array(
+ 'total' => 7843336192,
+ 'free' => 3758297088,
+ ),
+ );
+
+ $dir = dirname(__FILE__).'/memory';
+ foreach ($tests as $input => $expect) {
+ $raw = Filesystem::readFile($dir.'/'.$input);
+ $actual = PhutilSystem::parseMemInfo($raw);
+ $this->assertEqual(
+ $expect,
+ $actual,
+ pht('Parse of "%s".', $input));
+ }
+ }
+
+}
diff --git a/src/utils/__tests__/memory/meminfo.ubuntu14.txt b/src/utils/__tests__/memory/meminfo.ubuntu14.txt
new file mode 100644
--- /dev/null
+++ b/src/utils/__tests__/memory/meminfo.ubuntu14.txt
@@ -0,0 +1,42 @@
+MemTotal: 7659508 kB
+MemFree: 246684 kB
+Buffers: 126580 kB
+Cached: 3296948 kB
+SwapCached: 0 kB
+Active: 4916076 kB
+Inactive: 1732880 kB
+Active(anon): 3225504 kB
+Inactive(anon): 20576 kB
+Active(file): 1690572 kB
+Inactive(file): 1712304 kB
+Unevictable: 0 kB
+Mlocked: 0 kB
+SwapTotal: 0 kB
+SwapFree: 0 kB
+Dirty: 32 kB
+Writeback: 0 kB
+AnonPages: 3225428 kB
+Mapped: 37500 kB
+Shmem: 20652 kB
+Slab: 633908 kB
+SReclaimable: 467472 kB
+SUnreclaim: 166436 kB
+KernelStack: 2416 kB
+PageTables: 64744 kB
+NFS_Unstable: 0 kB
+Bounce: 0 kB
+WritebackTmp: 0 kB
+CommitLimit: 3829752 kB
+Committed_AS: 4935116 kB
+VmallocTotal: 34359738367 kB
+VmallocUsed: 17244 kB
+VmallocChunk: 34359712740 kB
+HardwareCorrupted: 0 kB
+AnonHugePages: 954368 kB
+HugePages_Total: 0
+HugePages_Free: 0
+HugePages_Rsvd: 0
+HugePages_Surp: 0
+Hugepagesize: 2048 kB
+DirectMap4k: 45056 kB
+DirectMap2M: 7950336 kB
diff --git a/src/utils/__tests__/memory/vmstat.yosemite.txt b/src/utils/__tests__/memory/vmstat.yosemite.txt
new file mode 100644
--- /dev/null
+++ b/src/utils/__tests__/memory/vmstat.yosemite.txt
@@ -0,0 +1,23 @@
+Mach Virtual Memory Statistics: (page size of 4096 bytes)
+Pages free: 422941.
+Pages active: 2348641.
+Pages inactive: 830440.
+Pages speculative: 110635.
+Pages throttled: 0.
+Pages wired down: 427172.
+Pages purgeable: 33368.
+"Translation faults": 931955891.
+Pages copy-on-write: 59498342.
+Pages zero filled: 411628732.
+Pages reactivated: 175636.
+Pages purged: 569552.
+File-backed pages: 926777.
+Anonymous pages: 2362939.
+Pages stored in compressor: 125673.
+Pages occupied by compressor: 51938.
+Decompressions: 32945.
+Compressions: 197789.
+Pageins: 13750115.
+Pageouts: 39562.
+Swapins: 0.
+Swapouts: 2290.

File Metadata

Mime Type
text/plain
Expires
Mon, Mar 17, 10:00 AM (1 w, 2 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7708222
Default Alt Text
D11863.id28576.diff (9 KB)

Event Timeline