| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 // Note: this file is used by Aura on all linux platforms, even though it | |
| 6 // is currently in a chromeos specific location. | |
| 7 | |
| 8 #include "chrome/browser/chromeos/status/memory_menu_button.h" | |
| 9 | |
| 10 #include "base/file_util.h" | |
| 11 #include "base/process_util.h" // GetSystemMemoryInfo | |
| 12 #include "base/stringprintf.h" | |
| 13 #include "base/threading/thread_restrictions.h" | |
| 14 #include "base/utf_string_conversions.h" | |
| 15 #include "chrome/browser/chromeos/view_ids.h" | |
| 16 #include "chrome/browser/memory_purger.h" | |
| 17 #include "chrome/common/render_messages.h" | |
| 18 #include "content/public/browser/notification_service.h" | |
| 19 #include "content/public/browser/notification_types.h" | |
| 20 #include "content/public/browser/render_process_host.h" | |
| 21 #include "grit/generated_resources.h" | |
| 22 #include "ui/base/l10n/l10n_util.h" | |
| 23 #include "ui/views/controls/menu/menu_runner.h" | |
| 24 #include "ui/views/widget/widget.h" | |
| 25 | |
| 26 #if defined(USE_TCMALLOC) | |
| 27 #include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h" | |
| 28 #endif | |
| 29 | |
| 30 #if defined(USE_TCMALLOC) | |
| 31 const char kProfileDumpFilePrefix[] = "/tmp/chrome_tcmalloc"; | |
| 32 #endif | |
| 33 | |
| 34 namespace { | |
| 35 | |
| 36 // views::MenuItemView item ids | |
| 37 enum { | |
| 38 MEM_TOTAL_ITEM, | |
| 39 MEM_FREE_ITEM, | |
| 40 MEM_BUFFERS_ITEM, | |
| 41 MEM_CACHE_ITEM, | |
| 42 SHMEM_ITEM, | |
| 43 PURGE_MEMORY_ITEM, | |
| 44 #if defined(USE_TCMALLOC) | |
| 45 TOGGLE_PROFILING_ITEM, | |
| 46 DUMP_PROFILING_ITEM, | |
| 47 #endif | |
| 48 }; | |
| 49 | |
| 50 } // namespace | |
| 51 | |
| 52 // Delay between updates, in seconds. | |
| 53 const int kUpdateIntervalSeconds = 5; | |
| 54 | |
| 55 MemoryMenuButton::MemoryMenuButton(StatusAreaButton::Delegate* delegate) | |
| 56 : StatusAreaButton(delegate, this), | |
| 57 meminfo_(new base::SystemMemoryInfoKB()), | |
| 58 renderer_kills_(0) { | |
| 59 set_id(VIEW_ID_STATUS_BUTTON_MEMORY); | |
| 60 // Track renderer kills, as the kernel OOM killer will start to kill our | |
| 61 // renderers as we run out of memory. | |
| 62 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, | |
| 63 content::NotificationService::AllSources()); | |
| 64 UpdateTextAndSetNextTimer(); | |
| 65 } | |
| 66 | |
| 67 MemoryMenuButton::~MemoryMenuButton() { | |
| 68 } | |
| 69 | |
| 70 void MemoryMenuButton::UpdateTextAndSetNextTimer() { | |
| 71 UpdateText(); | |
| 72 | |
| 73 timer_.Start(FROM_HERE, | |
| 74 base::TimeDelta::FromSeconds(kUpdateIntervalSeconds), this, | |
| 75 &MemoryMenuButton::UpdateTextAndSetNextTimer); | |
| 76 } | |
| 77 | |
| 78 void MemoryMenuButton::UpdateText() { | |
| 79 base::GetSystemMemoryInfo(meminfo_.get()); | |
| 80 // "Anonymous" memory, meaning not mapped to a file (which has a name), | |
| 81 // represents memory that has been dynamically allocated to a process. | |
| 82 // It thus approximates heap memory usage across all processes. | |
| 83 int anon_kb = meminfo_->active_anon + meminfo_->inactive_anon; | |
| 84 std::string label = base::StringPrintf("%d MB (%d)", | |
| 85 anon_kb / 1024, | |
| 86 renderer_kills_); | |
| 87 SetText(ASCIIToUTF16(label)); | |
| 88 std::string tooltip = base::StringPrintf("%d MB allocated (anonymous)\n" | |
| 89 "%d renderer kill(s)", | |
| 90 anon_kb / 1024, | |
| 91 renderer_kills_); | |
| 92 SetTooltipText(ASCIIToUTF16(tooltip)); | |
| 93 SchedulePaint(); | |
| 94 } | |
| 95 | |
| 96 // MemoryMenuButton, views::MenuDelegate implementation: | |
| 97 string16 MemoryMenuButton::GetLabel(int id) const { | |
| 98 std::string label; | |
| 99 switch (id) { | |
| 100 case MEM_TOTAL_ITEM: | |
| 101 label = base::StringPrintf("%d MB total", meminfo_->total / 1024); | |
| 102 break; | |
| 103 case MEM_FREE_ITEM: | |
| 104 label = base::StringPrintf("%d MB free", meminfo_->free / 1024); | |
| 105 break; | |
| 106 case MEM_BUFFERS_ITEM: | |
| 107 label = base::StringPrintf("%d MB buffers", meminfo_->buffers / 1024); | |
| 108 break; | |
| 109 case MEM_CACHE_ITEM: | |
| 110 label = base::StringPrintf("%d MB cache", meminfo_->cached / 1024); | |
| 111 break; | |
| 112 case SHMEM_ITEM: | |
| 113 label = base::StringPrintf("%d MB shmem", meminfo_->shmem / 1024); | |
| 114 break; | |
| 115 case PURGE_MEMORY_ITEM: | |
| 116 return ASCIIToUTF16("Purge memory"); | |
| 117 #if defined(USE_TCMALLOC) | |
| 118 case TOGGLE_PROFILING_ITEM: | |
| 119 if (!IsHeapProfilerRunning()) | |
| 120 return ASCIIToUTF16("Start profiling"); | |
| 121 else | |
| 122 return ASCIIToUTF16("Stop profiling"); | |
| 123 case DUMP_PROFILING_ITEM: | |
| 124 return ASCIIToUTF16("Dump profile"); | |
| 125 #endif | |
| 126 default: | |
| 127 return string16(); | |
| 128 } | |
| 129 return UTF8ToUTF16(label); | |
| 130 } | |
| 131 | |
| 132 bool MemoryMenuButton::IsCommandEnabled(int id) const { | |
| 133 switch (id) { | |
| 134 case PURGE_MEMORY_ITEM: | |
| 135 return true; | |
| 136 #if defined(USE_TCMALLOC) | |
| 137 case TOGGLE_PROFILING_ITEM: | |
| 138 case DUMP_PROFILING_ITEM: | |
| 139 return true; | |
| 140 #endif | |
| 141 default: | |
| 142 return false; | |
| 143 } | |
| 144 } | |
| 145 | |
| 146 namespace { | |
| 147 #if defined(USE_TCMALLOC) | |
| 148 FilePath::StringType GetProfileDumpFilePath(base::ProcessId pid) { | |
| 149 int int_pid = static_cast<int>(pid); | |
| 150 FilePath::StringType filepath = StringPrintf( | |
| 151 FILE_PATH_LITERAL("%s.%d.heap"), | |
| 152 FILE_PATH_LITERAL(kProfileDumpFilePrefix), int_pid); | |
| 153 return filepath; | |
| 154 } | |
| 155 #endif | |
| 156 } | |
| 157 | |
| 158 void MemoryMenuButton::SendCommandToRenderers(int id) { | |
| 159 #if defined(USE_TCMALLOC) | |
| 160 // Use the "is running" value for this process to determine whether to | |
| 161 // start or stop profiling on the renderer processes. | |
| 162 bool started = IsHeapProfilerRunning(); | |
| 163 for (content::RenderProcessHost::iterator it = | |
| 164 content::RenderProcessHost::AllHostsIterator(); | |
| 165 !it.IsAtEnd(); it.Advance()) { | |
| 166 switch (id) { | |
| 167 case TOGGLE_PROFILING_ITEM: | |
| 168 it.GetCurrentValue()->Send(new ChromeViewMsg_SetTcmallocHeapProfiling( | |
| 169 started, std::string(kProfileDumpFilePrefix))); | |
| 170 break; | |
| 171 case DUMP_PROFILING_ITEM: | |
| 172 it.GetCurrentValue()->Send(new ChromeViewMsg_WriteTcmallocHeapProfile( | |
| 173 GetProfileDumpFilePath( | |
| 174 base::GetProcId(it.GetCurrentValue()->GetHandle())))); | |
| 175 break; | |
| 176 default: | |
| 177 NOTREACHED(); | |
| 178 } | |
| 179 } | |
| 180 #endif | |
| 181 } | |
| 182 | |
| 183 void MemoryMenuButton::ExecuteCommand(int id) { | |
| 184 switch (id) { | |
| 185 case PURGE_MEMORY_ITEM: | |
| 186 MemoryPurger::PurgeAll(); | |
| 187 break; | |
| 188 #if defined(USE_TCMALLOC) | |
| 189 case TOGGLE_PROFILING_ITEM: { | |
| 190 if (!IsHeapProfilerRunning()) | |
| 191 HeapProfilerStart(kProfileDumpFilePrefix); | |
| 192 else | |
| 193 HeapProfilerStop(); | |
| 194 SendCommandToRenderers(id); | |
| 195 break; | |
| 196 } | |
| 197 case DUMP_PROFILING_ITEM: { | |
| 198 char* profile = GetHeapProfile(); | |
| 199 if (profile) { | |
| 200 FilePath::StringType filepath = | |
| 201 GetProfileDumpFilePath(base::GetProcId(base::GetCurrentProcId())); | |
| 202 VLOG(0) << "Writing browser heap profile dump to: " << filepath; | |
| 203 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
| 204 file_util::WriteFile(FilePath(filepath), profile, strlen(profile)); | |
| 205 delete profile; | |
| 206 } | |
| 207 SendCommandToRenderers(id); | |
| 208 break; | |
| 209 } | |
| 210 #endif | |
| 211 default: | |
| 212 NOTREACHED(); | |
| 213 break; | |
| 214 } | |
| 215 } | |
| 216 | |
| 217 int MemoryMenuButton::horizontal_padding() { | |
| 218 return 3; | |
| 219 } | |
| 220 | |
| 221 // MemoryMenuButton, views::MenuButtonListener implementation: | |
| 222 | |
| 223 void MemoryMenuButton::OnMenuButtonClicked(views::View* source, | |
| 224 const gfx::Point& point) { | |
| 225 // View passed in must be a views::MenuButton, i.e. the MemoryMenuButton. | |
| 226 DCHECK_EQ(source, this); | |
| 227 | |
| 228 menu_runner_.reset(new views::MenuRunner(CreateMenu())); | |
| 229 gfx::Point screen_location; | |
| 230 views::View::ConvertPointToScreen(source, &screen_location); | |
| 231 gfx::Rect bounds(screen_location, source->size()); | |
| 232 if (menu_runner_->RunMenuAt( | |
| 233 source->GetWidget()->GetTopLevelWidget(), this, bounds, | |
| 234 views::MenuItemView::TOPRIGHT, views::MenuRunner::HAS_MNEMONICS) == | |
| 235 views::MenuRunner::MENU_DELETED) | |
| 236 return; | |
| 237 } | |
| 238 | |
| 239 views::MenuItemView* MemoryMenuButton::CreateMenu() { | |
| 240 // Just rebuild the menu each time to ensure the labels are up-to-date. | |
| 241 views::MenuItemView* menu = new views::MenuItemView(this); | |
| 242 // Text for these items will be set by GetLabel(). | |
| 243 menu->AppendDelegateMenuItem(MEM_TOTAL_ITEM); | |
| 244 menu->AppendDelegateMenuItem(MEM_FREE_ITEM); | |
| 245 menu->AppendDelegateMenuItem(MEM_BUFFERS_ITEM); | |
| 246 menu->AppendDelegateMenuItem(MEM_CACHE_ITEM); | |
| 247 menu->AppendDelegateMenuItem(SHMEM_ITEM); | |
| 248 menu->AppendSeparator(); | |
| 249 menu->AppendDelegateMenuItem(PURGE_MEMORY_ITEM); | |
| 250 #if defined(USE_TCMALLOC) | |
| 251 menu->AppendSeparator(); | |
| 252 menu->AppendDelegateMenuItem(TOGGLE_PROFILING_ITEM); | |
| 253 if (IsHeapProfilerRunning()) | |
| 254 menu->AppendDelegateMenuItem(DUMP_PROFILING_ITEM); | |
| 255 #endif | |
| 256 return menu; | |
| 257 } | |
| 258 | |
| 259 ///////////////////////////////////////////////////////////////////////////// | |
| 260 // content::NotificationObserver overrides. | |
| 261 | |
| 262 void MemoryMenuButton::Observe(int type, | |
| 263 const content::NotificationSource& source, | |
| 264 const content::NotificationDetails& details) { | |
| 265 switch (type) { | |
| 266 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: { | |
| 267 content::RenderProcessHost::RendererClosedDetails* process_details = | |
| 268 content::Details<content::RenderProcessHost::RendererClosedDetails>( | |
| 269 details).ptr(); | |
| 270 if (process_details->status == | |
| 271 base::TERMINATION_STATUS_PROCESS_WAS_KILLED) { | |
| 272 renderer_kills_++; | |
| 273 // A kill is a very interesting event, so repaint immediately. | |
| 274 UpdateText(); | |
| 275 } | |
| 276 break; | |
| 277 } | |
| 278 default: | |
| 279 NOTREACHED() << L"Received unexpected notification"; | |
| 280 break; | |
| 281 } | |
| 282 } | |
| OLD | NEW |