Commit 768e4ff6 authored by stevenjb@google.com's avatar stevenjb@google.com

Enable tcmalloc profiling and heap dump.

BUG=chromium-os:18876
TEST=See issue.

Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=96054

Review URL: http://codereview.chromium.org/7528016

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@96444 0039d316-1c4b-4281-b951-d872f2087c98
parent 36a787fb
...@@ -4,16 +4,27 @@ ...@@ -4,16 +4,27 @@
#include "chrome/browser/chromeos/status/memory_menu_button.h" #include "chrome/browser/chromeos/status/memory_menu_button.h"
#include "base/file_util.h"
#include "base/process_util.h" // GetSystemMemoryInfo #include "base/process_util.h" // GetSystemMemoryInfo
#include "base/stringprintf.h" #include "base/stringprintf.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/chromeos/status/status_area_host.h" #include "chrome/browser/chromeos/status/status_area_host.h"
#include "chrome/browser/memory_purger.h" #include "chrome/browser/memory_purger.h"
#include "chrome/common/render_messages.h"
#include "content/browser/renderer_host/render_process_host.h" #include "content/browser/renderer_host/render_process_host.h"
#include "content/common/notification_service.h" #include "content/common/notification_service.h"
#include "grit/generated_resources.h" #include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
#include "views/widget/widget.h" #include "views/widget/widget.h"
#if defined(USE_TCMALLOC)
#include "third_party/tcmalloc/chromium/src/google/heap-profiler.h"
#endif
#if defined(USE_TCMALLOC)
const char kProfileDumpFilePrefix[] = "/tmp/chrome_tcmalloc";
#endif
namespace { namespace {
// views::MenuItemView item ids // views::MenuItemView item ids
...@@ -24,6 +35,10 @@ enum { ...@@ -24,6 +35,10 @@ enum {
MEM_CACHE_ITEM, MEM_CACHE_ITEM,
SHMEM_ITEM, SHMEM_ITEM,
PURGE_MEMORY_ITEM, PURGE_MEMORY_ITEM,
#if defined(USE_TCMALLOC)
TOGGLE_PROFILING_ITEM,
DUMP_PROFILING_ITEM,
#endif
}; };
} // namespace } // namespace
...@@ -88,6 +103,15 @@ std::wstring MemoryMenuButton::GetLabel(int id) const { ...@@ -88,6 +103,15 @@ std::wstring MemoryMenuButton::GetLabel(int id) const {
return StringPrintf(L"%d MB shmem", meminfo_->shmem / 1024); return StringPrintf(L"%d MB shmem", meminfo_->shmem / 1024);
case PURGE_MEMORY_ITEM: case PURGE_MEMORY_ITEM:
return L"Purge memory"; return L"Purge memory";
#if defined(USE_TCMALLOC)
case TOGGLE_PROFILING_ITEM:
if (!IsHeapProfilerRunning())
return L"Start profiling";
else
return L"Stop profiling";
case DUMP_PROFILING_ITEM:
return L"Dump profile";
#endif
default: default:
return std::wstring(); return std::wstring();
} }
...@@ -97,16 +121,80 @@ bool MemoryMenuButton::IsCommandEnabled(int id) const { ...@@ -97,16 +121,80 @@ bool MemoryMenuButton::IsCommandEnabled(int id) const {
switch (id) { switch (id) {
case PURGE_MEMORY_ITEM: case PURGE_MEMORY_ITEM:
return true; return true;
#if defined(USE_TCMALLOC)
case TOGGLE_PROFILING_ITEM:
case DUMP_PROFILING_ITEM:
return true;
#endif
default: default:
return false; return false;
} }
} }
namespace {
#if defined(USE_TCMALLOC)
FilePath::StringType GetProfileDumpFilePath(base::ProcessId pid) {
int int_pid = static_cast<int>(pid);
FilePath::StringType filepath = StringPrintf(
FILE_PATH_LITERAL("%s.%d.heap"),
FILE_PATH_LITERAL(kProfileDumpFilePrefix), int_pid);
return filepath;
}
#endif
}
void MemoryMenuButton::SendCommandToRenderers(int id) {
#if defined(USE_TCMALLOC)
// Use the "is running" value for this process to determine whether to
// start or stop profiling on the renderer processes.
bool started = IsHeapProfilerRunning();
for (RenderProcessHost::iterator it = RenderProcessHost::AllHostsIterator();
!it.IsAtEnd(); it.Advance()) {
switch (id) {
case TOGGLE_PROFILING_ITEM:
it.GetCurrentValue()->Send(new ViewMsg_SetTcmallocHeapProfiling(
started, std::string(kProfileDumpFilePrefix)));
break;
case DUMP_PROFILING_ITEM:
it.GetCurrentValue()->Send(new ViewMsg_WriteTcmallocHeapProfile(
GetProfileDumpFilePath(
base::GetProcId(it.GetCurrentValue()->GetHandle()))));
break;
default:
NOTREACHED();
}
}
#endif
}
void MemoryMenuButton::ExecuteCommand(int id) { void MemoryMenuButton::ExecuteCommand(int id) {
switch (id) { switch (id) {
case PURGE_MEMORY_ITEM: case PURGE_MEMORY_ITEM:
MemoryPurger::PurgeAll(); MemoryPurger::PurgeAll();
break; break;
#if defined(USE_TCMALLOC)
case TOGGLE_PROFILING_ITEM: {
if (!IsHeapProfilerRunning())
HeapProfilerStart(kProfileDumpFilePrefix);
else
HeapProfilerStop();
SendCommandToRenderers(id);
break;
}
case DUMP_PROFILING_ITEM: {
char* profile = GetHeapProfile();
if (profile) {
FilePath::StringType filepath =
GetProfileDumpFilePath(base::GetProcId(base::GetCurrentProcId()));
VLOG(0) << "Writing browser heap profile dump to: " << filepath;
base::ThreadRestrictions::ScopedAllowIO allow_io;
file_util::WriteFile(FilePath(filepath), profile, strlen(profile));
delete profile;
}
SendCommandToRenderers(id);
break;
}
#endif
default: default:
NOTREACHED(); NOTREACHED();
break; break;
...@@ -147,9 +235,14 @@ void MemoryMenuButton::EnsureMenu() { ...@@ -147,9 +235,14 @@ void MemoryMenuButton::EnsureMenu() {
menu_->AppendDelegateMenuItem(MEM_BUFFERS_ITEM); menu_->AppendDelegateMenuItem(MEM_BUFFERS_ITEM);
menu_->AppendDelegateMenuItem(MEM_CACHE_ITEM); menu_->AppendDelegateMenuItem(MEM_CACHE_ITEM);
menu_->AppendDelegateMenuItem(SHMEM_ITEM); menu_->AppendDelegateMenuItem(SHMEM_ITEM);
// TODO(jamescook): Dump heap profiles?
menu_->AppendSeparator(); menu_->AppendSeparator();
menu_->AppendDelegateMenuItem(PURGE_MEMORY_ITEM); menu_->AppendDelegateMenuItem(PURGE_MEMORY_ITEM);
#if defined(USE_TCMALLOC)
menu_->AppendSeparator();
menu_->AppendDelegateMenuItem(TOGGLE_PROFILING_ITEM);
if (IsHeapProfilerRunning())
menu_->AppendDelegateMenuItem(DUMP_PROFILING_ITEM);
#endif
} }
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
......
...@@ -55,6 +55,9 @@ class MemoryMenuButton : public StatusAreaButton, ...@@ -55,6 +55,9 @@ class MemoryMenuButton : public StatusAreaButton,
virtual int horizontal_padding() OVERRIDE; virtual int horizontal_padding() OVERRIDE;
private: private:
// Execute command id for each renderer. Used for heap profiling.
void SendCommandToRenderers(int id);
// Create and initialize menu if not already present. // Create and initialize menu if not already present.
void EnsureMenu(); void EnsureMenu();
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
#include "chrome/browser/renderer_host/chrome_render_message_filter.h" #include "chrome/browser/renderer_host/chrome_render_message_filter.h"
#include "base/file_path.h" #include "base/file_util.h"
#include "base/metrics/histogram.h" #include "base/metrics/histogram.h"
#include "chrome/browser/automation/automation_resource_message_filter.h" #include "chrome/browser/automation/automation_resource_message_filter.h"
#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process.h"
...@@ -121,6 +121,8 @@ bool ChromeRenderMessageFilter::OnMessageReceived(const IPC::Message& message, ...@@ -121,6 +121,8 @@ bool ChromeRenderMessageFilter::OnMessageReceived(const IPC::Message& message,
OnExtensionRequestForIOThread) OnExtensionRequestForIOThread)
#if defined(USE_TCMALLOC) #if defined(USE_TCMALLOC)
IPC_MESSAGE_HANDLER(ViewHostMsg_RendererTcmalloc, OnRendererTcmalloc) IPC_MESSAGE_HANDLER(ViewHostMsg_RendererTcmalloc, OnRendererTcmalloc)
IPC_MESSAGE_HANDLER(ViewHostMsg_WriteTcmallocHeapProfile_ACK,
OnWriteTcmallocHeapProfile)
#endif #endif
IPC_MESSAGE_HANDLER(ViewHostMsg_GetPluginPolicies, OnGetPluginPolicies) IPC_MESSAGE_HANDLER(ViewHostMsg_GetPluginPolicies, OnGetPluginPolicies)
IPC_MESSAGE_HANDLER(ViewHostMsg_AllowDatabase, OnAllowDatabase) IPC_MESSAGE_HANDLER(ViewHostMsg_AllowDatabase, OnAllowDatabase)
...@@ -384,10 +386,17 @@ void ChromeRenderMessageFilter::OnExtensionRequestForIOThread( ...@@ -384,10 +386,17 @@ void ChromeRenderMessageFilter::OnExtensionRequestForIOThread(
} }
#if defined(USE_TCMALLOC) #if defined(USE_TCMALLOC)
void ChromeRenderMessageFilter::OnRendererTcmalloc(base::ProcessId pid, void ChromeRenderMessageFilter::OnRendererTcmalloc(const std::string& output) {
const std::string& output) { base::ProcessId pid = base::GetProcId(peer_handle());
AboutTcmallocRendererCallback(pid, output); AboutTcmallocRendererCallback(pid, output);
} }
void ChromeRenderMessageFilter::OnWriteTcmallocHeapProfile(
const FilePath::StringType& filepath,
const std::string& output) {
VLOG(0) << "Writing renderer heap profile dump to: " << filepath;
file_util::WriteFile(FilePath(filepath), output.c_str(), output.size());
}
#endif #endif
void ChromeRenderMessageFilter::OnGetPluginPolicies( void ChromeRenderMessageFilter::OnGetPluginPolicies(
......
...@@ -88,7 +88,9 @@ class ChromeRenderMessageFilter : public BrowserMessageFilter { ...@@ -88,7 +88,9 @@ class ChromeRenderMessageFilter : public BrowserMessageFilter {
int routing_id, int routing_id,
const ExtensionHostMsg_Request_Params& params); const ExtensionHostMsg_Request_Params& params);
#if defined(USE_TCMALLOC) #if defined(USE_TCMALLOC)
void OnRendererTcmalloc(base::ProcessId pid, const std::string& output); void OnRendererTcmalloc(const std::string& output);
void OnWriteTcmallocHeapProfile(const FilePath::StringType& filename,
const std::string& output);
#endif #endif
void OnGetPluginPolicies(ContentSetting* outdated_policy, void OnGetPluginPolicies(ContentSetting* outdated_policy,
ContentSetting* authorize_policy); ContentSetting* authorize_policy);
......
...@@ -211,6 +211,16 @@ IPC_MESSAGE_CONTROL2(ViewMsg_SetFieldTrialGroup, ...@@ -211,6 +211,16 @@ IPC_MESSAGE_CONTROL2(ViewMsg_SetFieldTrialGroup,
#if defined(USE_TCMALLOC) #if defined(USE_TCMALLOC)
// Asks the renderer to send back tcmalloc stats. // Asks the renderer to send back tcmalloc stats.
IPC_MESSAGE_CONTROL0(ViewMsg_GetRendererTcmalloc) IPC_MESSAGE_CONTROL0(ViewMsg_GetRendererTcmalloc)
// Asks the renderer to enable/disable Tcmalloc heap profiling.
// Note: filename_prefix arg is effectively ignored since the render process
// will be unable to write files to disk. Instead use WriteTcmallocHeapProfile
// to write a profile file.
IPC_MESSAGE_CONTROL2(ViewMsg_SetTcmallocHeapProfiling,
bool /* enable profiling */,
std::string /* filename prefix for profiles */)
// Asks the renderer to write the Tcmalloc heap profile to a file.
IPC_MESSAGE_CONTROL1(ViewMsg_WriteTcmallocHeapProfile,
FilePath::StringType /* filepath */)
#endif #endif
// Asks the renderer to send back V8 heap stats. // Asks the renderer to send back V8 heap stats.
...@@ -414,9 +424,12 @@ IPC_MESSAGE_CONTROL2(ViewHostMsg_RendererHistograms, ...@@ -414,9 +424,12 @@ IPC_MESSAGE_CONTROL2(ViewHostMsg_RendererHistograms,
#if defined USE_TCMALLOC #if defined USE_TCMALLOC
// Send back tcmalloc stats output. // Send back tcmalloc stats output.
IPC_MESSAGE_CONTROL2(ViewHostMsg_RendererTcmalloc, IPC_MESSAGE_CONTROL1(ViewHostMsg_RendererTcmalloc,
int /* pid */,
std::string /* tcmalloc debug output */) std::string /* tcmalloc debug output */)
// Send back tcmalloc profile to write to a file.
IPC_MESSAGE_CONTROL2(ViewHostMsg_WriteTcmallocHeapProfile_ACK,
FilePath::StringType /* filepath */,
std::string /* heap profile */)
#endif #endif
// Sends back stats about the V8 heap. // Sends back stats about the V8 heap.
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include "net/base/net_module.h" #include "net/base/net_module.h"
#include "third_party/sqlite/sqlite3.h" #include "third_party/sqlite/sqlite3.h"
#include "third_party/tcmalloc/chromium/src/google/malloc_extension.h" #include "third_party/tcmalloc/chromium/src/google/malloc_extension.h"
#include "third_party/tcmalloc/chromium/src/google/heap-profiler.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebCache.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebCache.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebCrossOriginPreflightResultCache.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebCrossOriginPreflightResultCache.h"
...@@ -407,6 +408,10 @@ bool ChromeRenderProcessObserver::OnControlMessageReceived( ...@@ -407,6 +408,10 @@ bool ChromeRenderProcessObserver::OnControlMessageReceived(
IPC_MESSAGE_HANDLER(ViewMsg_SetFieldTrialGroup, OnSetFieldTrialGroup) IPC_MESSAGE_HANDLER(ViewMsg_SetFieldTrialGroup, OnSetFieldTrialGroup)
#if defined(USE_TCMALLOC) #if defined(USE_TCMALLOC)
IPC_MESSAGE_HANDLER(ViewMsg_GetRendererTcmalloc, OnGetRendererTcmalloc) IPC_MESSAGE_HANDLER(ViewMsg_GetRendererTcmalloc, OnGetRendererTcmalloc)
IPC_MESSAGE_HANDLER(ViewMsg_SetTcmallocHeapProfiling,
OnSetTcmallocHeapProfiling)
IPC_MESSAGE_HANDLER(ViewMsg_WriteTcmallocHeapProfile,
OnWriteTcmallocHeapProfile)
#endif #endif
IPC_MESSAGE_HANDLER(ViewMsg_GetV8HeapStats, OnGetV8HeapStats) IPC_MESSAGE_HANDLER(ViewMsg_GetV8HeapStats, OnGetV8HeapStats)
IPC_MESSAGE_HANDLER(ViewMsg_GetCacheResourceStats, OnGetCacheResourceStats) IPC_MESSAGE_HANDLER(ViewMsg_GetCacheResourceStats, OnGetCacheResourceStats)
...@@ -458,11 +463,41 @@ void ChromeRenderProcessObserver::OnGetCacheResourceStats() { ...@@ -458,11 +463,41 @@ void ChromeRenderProcessObserver::OnGetCacheResourceStats() {
void ChromeRenderProcessObserver::OnGetRendererTcmalloc() { void ChromeRenderProcessObserver::OnGetRendererTcmalloc() {
std::string result; std::string result;
char buffer[1024 * 32]; char buffer[1024 * 32];
base::ProcessId pid = base::GetCurrentProcId();
MallocExtension::instance()->GetStats(buffer, sizeof(buffer)); MallocExtension::instance()->GetStats(buffer, sizeof(buffer));
result.append(buffer); result.append(buffer);
Send(new ViewHostMsg_RendererTcmalloc(pid, result)); Send(new ViewHostMsg_RendererTcmalloc(result));
} }
void ChromeRenderProcessObserver::OnSetTcmallocHeapProfiling(
bool profiling, const std::string& filename_prefix) {
#if !defined(OS_WIN)
// TODO(stevenjb): Create MallocExtension wrappers for HeapProfile functions.
if (profiling)
HeapProfilerStart(filename_prefix.c_str());
else
HeapProfilerStop();
#endif
}
void ChromeRenderProcessObserver::OnWriteTcmallocHeapProfile(
const FilePath::StringType& filename) {
#if !defined(OS_WIN)
// TODO(stevenjb): Create MallocExtension wrappers for HeapProfile functions.
if (!IsHeapProfilerRunning())
return;
char* profile = GetHeapProfile();
if (!profile) {
LOG(WARNING) << "Unable to get heap profile.";
return;
}
// The render process can not write to a file, so copy the result into
// a string and pass it to the handler (which runs on the browser host).
std::string result(profile);
delete profile;
Send(new ViewHostMsg_WriteTcmallocHeapProfile_ACK(filename, result));
#endif
}
#endif #endif
void ChromeRenderProcessObserver::OnSetFieldTrialGroup( void ChromeRenderProcessObserver::OnSetFieldTrialGroup(
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <string> #include <string>
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/file_path.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "content/renderer/render_process_observer.h" #include "content/renderer/render_process_observer.h"
...@@ -49,6 +50,8 @@ class ChromeRenderProcessObserver : public RenderProcessObserver { ...@@ -49,6 +50,8 @@ class ChromeRenderProcessObserver : public RenderProcessObserver {
void OnSetFieldTrialGroup(const std::string& fiel_trial_name, void OnSetFieldTrialGroup(const std::string& fiel_trial_name,
const std::string& group_name); const std::string& group_name);
void OnGetRendererTcmalloc(); void OnGetRendererTcmalloc();
void OnSetTcmallocHeapProfiling(bool profiling, const std::string& prefix);
void OnWriteTcmallocHeapProfile(const FilePath::StringType& filename);
void OnGetV8HeapStats(); void OnGetV8HeapStats();
void OnPurgeMemory(); void OnPurgeMemory();
......
...@@ -36,6 +36,7 @@ include_rules = [ ...@@ -36,6 +36,7 @@ include_rules = [
"+third_party/gpsd", "+third_party/gpsd",
"+third_party/npapi/bindings", "+third_party/npapi/bindings",
"+third_party/sqlite", "+third_party/sqlite",
"+third_party/tcmalloc",
# Allow inclusion of WebKit API files. # Allow inclusion of WebKit API files.
"+third_party/WebKit/Source/WebKit/chromium", "+third_party/WebKit/Source/WebKit/chromium",
......
...@@ -30,6 +30,10 @@ ...@@ -30,6 +30,10 @@
#include "content/common/unix_domain_socket_posix.h" #include "content/common/unix_domain_socket_posix.h"
#include "sandbox/linux/suid/suid_unsafe_environment_variables.h" #include "sandbox/linux/suid/suid_unsafe_environment_variables.h"
#if defined(USE_TCMALLOC)
#include "third_party/tcmalloc/chromium/src/google/heap-profiler.h"
#endif
static void SaveSUIDUnsafeEnvironmentVariables() { static void SaveSUIDUnsafeEnvironmentVariables() {
// The ELF loader will clear many environment variables so we save them to // The ELF loader will clear many environment variables so we save them to
// different names here so that the SUID sandbox can resolve them for the // different names here so that the SUID sandbox can resolve them for the
...@@ -308,6 +312,13 @@ void ZygoteHost::AdjustRendererOOMScore(base::ProcessHandle pid, int score) { ...@@ -308,6 +312,13 @@ void ZygoteHost::AdjustRendererOOMScore(base::ProcessHandle pid, int score) {
} }
if (using_suid_sandbox_ && !selinux) { if (using_suid_sandbox_ && !selinux) {
#if defined(USE_TCMALLOC)
// If heap profiling is running, these processes are not exiting, at least
// on ChromeOS. The easiest thing to do is not launch them when profiling.
// TODO(stevenjb): Investigate further and fix.
if (IsHeapProfilerRunning())
return;
#endif
std::vector<std::string> adj_oom_score_cmdline; std::vector<std::string> adj_oom_score_cmdline;
adj_oom_score_cmdline.push_back(sandbox_binary_); adj_oom_score_cmdline.push_back(sandbox_binary_);
adj_oom_score_cmdline.push_back(base::kAdjustOOMScoreSwitch); adj_oom_score_cmdline.push_back(base::kAdjustOOMScoreSwitch);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment