1 /* 2 * MemUsageWidget.java 3 * 4 * Copyright (C) 2021 by RStudio, PBC 5 * 6 * Unless you have received this program directly from RStudio pursuant 7 * to the terms of a commercial license agreement with RStudio, then 8 * this program is licensed to you under the terms of version 3 of the 9 * GNU Affero General Public License. This program is distributed WITHOUT 10 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT, 11 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the 12 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details. 13 * 14 */ 15 package org.rstudio.studio.client.workbench.views.environment.view; 16 17 import com.google.gwt.dom.client.Style; 18 import com.google.gwt.i18n.client.NumberFormat; 19 import com.google.gwt.resources.client.ImageResource; 20 import com.google.gwt.user.client.ui.Composite; 21 import com.google.gwt.user.client.ui.HTMLPanel; 22 import com.google.gwt.user.client.ui.Widget; 23 import org.rstudio.core.client.ElementIds; 24 import org.rstudio.core.client.StringUtil; 25 import org.rstudio.core.client.widget.MiniPieWidget; 26 import org.rstudio.core.client.widget.ToolbarButton; 27 import org.rstudio.core.client.widget.ToolbarMenuButton; 28 import org.rstudio.core.client.widget.ToolbarPopupMenu; 29 import org.rstudio.core.client.widget.UserPrefMenuItem; 30 import org.rstudio.studio.client.RStudioGinjector; 31 import org.rstudio.studio.client.workbench.prefs.model.UserPrefs; 32 import org.rstudio.studio.client.workbench.views.environment.model.MemoryUsage; 33 34 public class MemUsageWidget extends Composite 35 { MemUsageWidget(MemoryUsage usage, UserPrefs prefs)36 public MemUsageWidget(MemoryUsage usage, UserPrefs prefs) 37 { 38 prefs_ = prefs; 39 suspended_ = false; 40 41 host_ = new HTMLPanel(""); 42 Style style = host_.getElement().getStyle(); 43 style.setProperty("display", "flex"); 44 style.setProperty("flexDirection", "row"); 45 46 pieCrust_ = new HTMLPanel(""); 47 pieCrust_.setHeight("13px"); 48 pieCrust_.setWidth("13px"); 49 style = pieCrust_.getElement().getStyle(); 50 style.setMarginTop(3, Style.Unit.PX); 51 style.setMarginRight(3, Style.Unit.PX); 52 host_.add(pieCrust_); 53 ElementIds.assignElementId(pieCrust_, ElementIds.MEMORY_PIE_MINI); 54 55 ToolbarPopupMenu memoryMenu = new ToolbarPopupMenu(); 56 memoryMenu.addItem(RStudioGinjector.INSTANCE.getCommands().freeUnusedMemory().createMenuItem(false)); 57 memoryMenu.addItem(RStudioGinjector.INSTANCE.getCommands().showMemoryUsageReport().createMenuItem(false)); 58 memoryMenu.addSeparator(); 59 memoryMenu.addItem(new UserPrefMenuItem<Boolean>( 60 prefs_.showMemoryUsage(), 61 true, 62 "Show Current Memory Usage", 63 prefs_ 64 )); 65 66 menu_ = new ToolbarMenuButton( 67 "Mem", 68 ToolbarButton.NoTitle, 69 (ImageResource) null, 70 memoryMenu); 71 ElementIds.assignElementId(menu_, ElementIds.MEMORY_DROPDOWN); 72 menu_.getElement().getStyle().setMarginTop(-3, Style.Unit.PX); 73 host_.add(menu_); 74 75 setMemoryUsage(usage); 76 77 prefs.showMemoryUsage().addValueChangeHandler(evt -> 78 { 79 // Clear memory usage display immediately when turned off 80 if (!evt.getValue()) 81 { 82 setMemoryUsage(null); 83 } 84 }); 85 86 initWidget(host_); 87 } 88 89 /** 90 * Sets the memory usage to be displayed in the widget. 91 * 92 * @param usage The memory usage to display 93 */ setMemoryUsage(MemoryUsage usage)94 public void setMemoryUsage(MemoryUsage usage) 95 { 96 usage_ = usage; 97 98 if (usage == null) 99 { 100 pieCrust_.getElement().removeAllChildren(); 101 pieCrust_.setVisible(false); 102 menu_.setText("Mem"); 103 } 104 else 105 { 106 // If we were previously showing suspended memory data, then switch to live session view 107 if (suspended_) 108 { 109 setSuspended(false); 110 } 111 112 menu_.setTitle(StringUtil.prettyFormatNumber(usage.getProcess().getKb()) + 113 " KiB used by R session (source: " + usage.getProcess().getProviderName() + ")"); 114 menu_.setText(formatBigMemory(usage.getProcess().getKb())); 115 116 MemoryUsagePieChart pie = new MemoryUsagePieChart(usage); 117 loadPieDisplay(pie); 118 } 119 } 120 121 /** 122 * Load a pie chart into the memory usage widget 123 * 124 * @param pie The pie chart to load 125 */ loadPieDisplay(Widget pie)126 private void loadPieDisplay(Widget pie) 127 { 128 // For browser SVG painting reasons, it is necessary to create a wholly 129 // new SVG element and then replay it as HTML into the DOM to get it to 130 // draw correctly. 131 pieCrust_.setVisible(true); 132 pieCrust_.getElement().removeAllChildren(); 133 pieCrust_.add(pie); 134 pieCrust_.getElement().setInnerHTML(pieCrust_.getElement().getInnerHTML()); 135 } 136 137 /** 138 * Formats a large memory statistic for memory display 139 * 140 * @param kb The amount of memory 141 * @return A string describing the amount of memory 142 */ formatBigMemory(int kb)143 private String formatBigMemory(int kb) 144 { 145 long mib = kb / 1024; 146 if (mib >= 1024) 147 { 148 // Memory usage is > 1GiB, format as XX.YY GiB 149 NumberFormat decimalFormat = NumberFormat.getFormat(".##"); 150 return decimalFormat.format((double)mib / (double)1024) + " GiB"; 151 } 152 153 // Memory usage is in MiB 154 return mib + " MiB"; 155 } 156 157 /** 158 * Sets whether to show the memory usage as suspended 159 * 160 * @param suspended 161 */ setSuspended(boolean suspended)162 public void setSuspended(boolean suspended) 163 { 164 // Ignore if we're already in the desired state 165 if (suspended == suspended_) 166 { 167 return; 168 } 169 suspended_ = suspended; 170 171 // If the widget isn't showing memory usage information 172 if (usage_ == null) 173 { 174 return; 175 } 176 177 if (suspended) 178 { 179 MiniPieWidget pie = new MiniPieWidget( 180 "Memory in use: none (suspended)", 181 "Empty pie chart depicting no memory usage", 182 MEMORY_PIE_UNUSED_COLOR); 183 loadPieDisplay(pie); 184 185 menu_.setEnabled(false); 186 } 187 else 188 { 189 menu_.setEnabled(true); 190 } 191 } 192 193 private final UserPrefs prefs_; 194 private final HTMLPanel pieCrust_; 195 private final ToolbarMenuButton menu_; 196 private final HTMLPanel host_; 197 198 private MemoryUsage usage_; 199 private boolean suspended_; 200 201 public static final String MEMORY_PIE_UNUSED_COLOR = "#e4e4e4"; 202 }