Permalink
Browse files

Display memory statistics upon crash.

  • Loading branch information...
1 parent 33d35b3 commit f6f0832c963b939459f2fee4f48fdb63c9c192b1 @FooBarWidget FooBarWidget committed Nov 26, 2012
@@ -53,44 +53,84 @@ setGivenEnvVars(const char *envvarsData) {
static void
dumpInformation() {
- const char *dir;
- if ((dir = getenv("PASSENGER_DEBUG_DIR")) != NULL) {
- FILE *f;
+ const char *c_dir;
+ if ((c_dir = getenv("PASSENGER_DEBUG_DIR")) == NULL) {
+ return;
+ }
- f = fopen((string(dir) + "/envvars").c_str(), "w");
- if (f != NULL) {
- int i = 0;
- while (environ[i] != NULL) {
- fputs(environ[i], f);
- putc('\n', f);
- i++;
- }
- fclose(f);
+ FILE *f;
+ string dir = c_dir;
+
+ f = fopen((dir + "/envvars").c_str(), "w");
+ if (f != NULL) {
+ int i = 0;
+ while (environ[i] != NULL) {
+ fputs(environ[i], f);
+ putc('\n', f);
+ i++;
}
+ fclose(f);
+ }
- f = fopen((string(dir) + "/user_info").c_str(), "w");
- if (f != NULL) {
- pid_t pid = fork();
- if (pid == 0) {
- dup2(fileno(f), 1);
- execlp("id", "id", (char *) 0);
- _exit(1);
- } else if (pid == -1) {
- int e = errno;
- fprintf(stderr, "Error: cannot fork a new process: %s (errno=%d)\n",
- strerror(e), e);
- } else {
- waitpid(pid, NULL, 0);
- }
- fclose(f);
+ f = fopen((dir + "/user_info").c_str(), "w");
+ if (f != NULL) {
+ pid_t pid = fork();
+ if (pid == 0) {
+ dup2(fileno(f), 1);
+ execlp("id", "id", (char *) 0);
+ _exit(1);
+ } else if (pid == -1) {
+ int e = errno;
+ fprintf(stderr, "Error: cannot fork a new process: %s (errno=%d)\n",
+ strerror(e), e);
+ } else {
+ waitpid(pid, NULL, 0);
}
+ fclose(f);
+ }
- f = fopen((string(dir) + "/ulimit").c_str(), "w");
+ f = fopen((dir + "/ulimit").c_str(), "w");
+ if (f != NULL) {
+ pid_t pid = fork();
+ if (pid == 0) {
+ dup2(fileno(f), 1);
+ execlp("ulimit", "ulimit", "-a", (char *) 0);
+ _exit(1);
+ } else if (pid == -1) {
+ int e = errno;
+ fprintf(stderr, "Error: cannot fork a new process: %s (errno=%d)\n",
+ strerror(e), e);
+ } else {
+ waitpid(pid, NULL, 0);
+ }
+ fclose(f);
+ }
+
+ f = fopen((dir + "/ulimit").c_str(), "w");
+ if (f != NULL) {
+ pid_t pid = fork();
+ if (pid == 0) {
+ dup2(fileno(f), 1);
+ execlp("ulimit", "ulimit", "-a", (char *) 0);
+ _exit(1);
+ } else if (pid == -1) {
+ int e = errno;
+ fprintf(stderr, "Error: cannot fork a new process: %s (errno=%d)\n",
+ strerror(e), e);
+ } else {
+ waitpid(pid, NULL, 0);
+ }
+ fclose(f);
+ }
+
+ #ifdef __linux__
+ // TODO: call helper-scripts/system-memory-stats.py
+ f = fopen((dir + "/sysmemory").c_str(), "w");
if (f != NULL) {
pid_t pid = fork();
if (pid == 0) {
dup2(fileno(f), 1);
- execlp("ulimit", "ulimit", "-a", (char *) 0);
+ execlp("free", "free", "-m", (char *) 0);
_exit(1);
} else if (pid == -1) {
int e = errno;
@@ -101,7 +141,7 @@ dumpInformation() {
}
fclose(f);
}
- }
+ #endif
}
// Usage: SpawnPreparer <envvars> <executable> <exec args...>
@@ -0,0 +1,60 @@
+#!/usr/bin/python
+import sys
+
+if sys.version_info[0] >= 3:
+ def bytes_to_str(b):
+ return b.decode()
+else:
+ def bytes_to_str(b):
+ return b
+
+def linux_memory_stats():
+ import os
+ os.execlp('free', 'free', '-m')
+
+def osx_memory_stats():
+ import subprocess, re, os, resource
+
+ def popen_read(command):
+ return bytes_to_str(subprocess.Popen(command, stdout = subprocess.PIPE).communicate()[0])
+
+ # Get process info
+ ps = popen_read(['ps', '-caxm', '-orss,comm'])
+ vm = popen_read(['vm_stat'])
+
+ # Iterate processes
+ process_lines = ps.split('\n')
+ sep = re.compile('[\s]+')
+ rss_total = 0 # kB
+ for row in range(1, len(process_lines)):
+ row_text = process_lines[row].strip()
+ row_elements = sep.split(row_text)
+ try:
+ rss = float(row_elements[0]) * 1024
+ except:
+ rss = 0 # ignore...
+ rss_total += rss
+
+ # Process vm_stat
+ vm_lines = vm.split('\n')
+ sep = re.compile(':[\s]+')
+ vm_stats = {}
+ for row in range(1, len(vm_lines) - 2):
+ row_text = vm_lines[row].strip()
+ row_elements = sep.split(row_text)
+ vm_stats[(row_elements[0])] = int(row_elements[1].strip('\.')) * resource.getpagesize()
+
+ print('---- Summary ----')
+ print('Wired Memory:\t\t%.1f MB' % (vm_stats["Pages wired down"] / 1024 / 1024))
+ print('Active Memory:\t\t%.1f MB' % (vm_stats["Pages active"] / 1024 / 1024))
+ print('Inactive Memory:\t%.1f MB' % (vm_stats["Pages inactive"] / 1024 / 1024))
+ print('Free Memory:\t\t%.1f MB' % (vm_stats["Pages free"] / 1024 / 1024))
+ print('Real Mem Total (ps):\t%.1f MB' % (rss_total / 1024 / 1024))
+ print('')
+ print('---- vm_stat ----')
+ print(vm.strip())
+
+if sys.platform.find('linux') > -1:
+ linux_memory_stats()
+elif sys.platform.find('darwin') > -1:
+ osx_memory_stats()
@@ -71,6 +71,7 @@ def sanitize_spawn_options(options)
def dump_all_information
dump_ruby_environment
dump_envvars
+ dump_system_memory_stats
end
def dump_ruby_environment
@@ -127,6 +128,16 @@ def dump_envvars
rescue SystemCallError
# Don't care.
end
+
+ def dump_system_memory_stats
+ if dir = ENV['PASSENGER_DEBUG_DIR']
+ File.open("#{dir}/sysmemory", "w") do |f|
+ f.write(`"#{PhusionPassenger.helper_scripts_dir}/system-memory-stats.py"`)
+ end
+ end
+ rescue SystemCallError
+ # Don't care.
+ end
# Prepare an application process using rules for the given spawn options.
# This method is to be called before loading the application code.
@@ -48,6 +48,10 @@
<dd><pre>{{ENVVARS|default=Unknown}}</pre></dd>
<dt>Ulimits</dt>
<dd><pre>{{ULIMIT|default=Unknown}}</pre></dd>
+ {{if SYSMEMORY}}
+ <dt>System memory usage</dt>
+ <dd><pre>{{SYSMEMORY|default=Unknown}}</pre></dd>
+ {{/if}}
{{if RUBY_INFO}}
<dt>General Ruby interpreter information</dt>
<dd><pre>{{RUBY_INFO}}</pre></dd>

0 comments on commit f6f0832

Please sign in to comment.