Commit a7b5e669 authored by droger's avatar droger Committed by Commit bot

[iOS] Upstream //ios/chrome/browser/memory

Review URL: https://codereview.chromium.org/1057933002

Cr-Commit-Position: refs/heads/master@{#324216}
parent 303b4f84
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef IOS_CHROME_BROWSER_MEMORY_MEMORY_DEBUGGER_H_
#define IOS_CHROME_BROWSER_MEMORY_MEMORY_DEBUGGER_H_
#import <UIKit/UIKit.h>
// A view that contains memory information (e.g. amount of free memory) and
// tools (e.g. trigger memory warning) to help investigate memory issues and
// performance.
//
// The debugger ensures that it remains visible by continuously calling
// bringSubviewToFront on it's parent so it should be added as a subview of the
// the application's window in order to stay visible all the times.
//
// The debugger owns some timers that must be invalidated before it can be
// deallocated so the owner must call |invalidateTimers| before a MemoryDebugger
// instance can be deallocated.
@interface MemoryDebugger : UIView<UITextFieldDelegate>
// Must be called before the object can be deallocated!
- (void)invalidateTimers;
@end
#endif // IOS_CHROME_BROWSER_MEMORY_MEMORY_DEBUGGER_H_
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/memory/memory_debugger.h"
#include "base/ios/ios_util.h"
#import "base/mac/scoped_nsobject.h"
#import "base/memory/scoped_ptr.h"
#import "ios/chrome/browser/memory/memory_metrics.h"
#include "ios/chrome/browser/ui/ui_util.h"
#import "ios/chrome/browser/ui/uikit_ui_util.h"
namespace {
// The number of bytes in a megabyte.
const CGFloat kNumBytesInMB = 1024 * 1024;
// The horizontal and vertical padding between subviews.
const CGFloat kPadding = 10;
} // namespace
@implementation MemoryDebugger {
// A timer to trigger refreshes.
base::scoped_nsobject<NSTimer> _refreshTimer;
// A timer to trigger continuous memory warnings.
base::scoped_nsobject<NSTimer> _memoryWarningTimer;
// The font to use.
base::scoped_nsobject<UIFont> _font;
// Labels for memory metrics.
base::scoped_nsobject<UILabel> _physicalFreeMemoryLabel;
base::scoped_nsobject<UILabel> _realMemoryUsedLabel;
base::scoped_nsobject<UILabel> _xcodeGaugeLabel;
base::scoped_nsobject<UILabel> _dirtyVirtualMemoryLabel;
// Inputs for memory commands.
base::scoped_nsobject<UITextField> _bloatField;
base::scoped_nsobject<UITextField> _refreshField;
base::scoped_nsobject<UITextField> _continuousMemoryWarningField;
// A place to store the artifical memory bloat.
scoped_ptr<uint8> _bloat;
// Distance the view was pushed up to accomodate the keyboard.
CGFloat _keyboardOffset;
// The current orientation of the device.
BOOL _currentOrientation;
}
- (instancetype)init {
self = [super initWithFrame:CGRectZero];
if (self) {
_font.reset([[UIFont systemFontOfSize:14] retain]);
self.backgroundColor = [UIColor colorWithWhite:0.8f alpha:0.9f];
self.opaque = NO;
[self addSubviews];
[self adjustForOrientation:nil];
[self sizeToFit];
[self registerForNotifications];
}
return self;
}
// NSTimers create a retain cycle so they must be invalidated before this
// instance can be deallocated.
- (void)invalidateTimers {
[_refreshTimer invalidate];
[_memoryWarningTimer invalidate];
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
#pragma mark UIView methods
- (CGSize)sizeThatFits:(CGSize)size {
CGFloat width = 0;
CGFloat height = 0;
for (UIView* subview in self.subviews) {
width = MAX(width, CGRectGetMaxX(subview.frame));
height = MAX(height, CGRectGetMaxY(subview.frame));
}
return CGSizeMake(width + kPadding, height + kPadding);
}
#pragma mark initialization helpers
- (void)addSubviews {
// |index| is used to calculate the vertical position of each element in
// the debugger view.
NSUInteger index = 0;
// Display some metrics.
_physicalFreeMemoryLabel.reset([[UILabel alloc] initWithFrame:CGRectZero]);
[self addMetricWithName:@"Physical Free"
atIndex:index++
usingLabel:_physicalFreeMemoryLabel];
_realMemoryUsedLabel.reset([[UILabel alloc] initWithFrame:CGRectZero]);
[self addMetricWithName:@"Real Memory Used"
atIndex:index++
usingLabel:_realMemoryUsedLabel];
_xcodeGaugeLabel.reset([[UILabel alloc] initWithFrame:CGRectZero]);
[self addMetricWithName:@"Xcode Gauge"
atIndex:index++
usingLabel:_xcodeGaugeLabel];
_dirtyVirtualMemoryLabel.reset([[UILabel alloc] initWithFrame:CGRectZero]);
[self addMetricWithName:@"Dirty VM"
atIndex:index++
usingLabel:_dirtyVirtualMemoryLabel];
// Since _performMemoryWarning is a private API it can't be compiled into
// official builds.
// TODO(lliabraa): Figure out how to support memory warnings (or something
// like them) in official builds.
#if CHROMIUM_BUILD
[self addButtonWithTitle:@"Trigger Memory Warning"
target:[UIApplication sharedApplication]
action:@selector(_performMemoryWarning)
withOrigin:[self originForSubviewAtIndex:index++]];
#endif // CHROMIUM_BUILD
// Display a text input to set the amount of artificial memory bloat and a
// button to reset the bloat to zero.
_bloatField.reset([[UITextField alloc] initWithFrame:CGRectZero]);
[self addLabelWithText:@"Set bloat (MB)"
input:_bloatField
inputTarget:self
inputAction:@selector(updateBloat)
buttonWithTitle:@"Clear"
buttonTarget:self
buttonAction:@selector(clearBloat)
atIndex:index++];
[_bloatField setText:@"0"];
[self updateBloat];
// Since _performMemoryWarning is a private API it can't be compiled into
// official builds.
// TODO(lliabraa): Figure out how to support memory warnings (or something
// like them) in official builds.
#if CHROMIUM_BUILD
// Display a text input to control the rate of continuous memory warnings.
_continuousMemoryWarningField.reset(
[[UITextField alloc] initWithFrame:CGRectZero]);
[self addLabelWithText:@"Set memory warning interval (secs)"
input:_continuousMemoryWarningField
inputTarget:self
inputAction:@selector(updateMemoryWarningInterval)
atIndex:index++];
[_continuousMemoryWarningField setText:@"0.0"];
#endif // CHROMIUM_BUILD
// Display a text input to control the refresh rate of the memory debugger.
_refreshField.reset([[UITextField alloc] initWithFrame:CGRectZero]);
[self addLabelWithText:@"Set refresh interval (secs)"
input:_refreshField
inputTarget:self
inputAction:@selector(updateRefreshInterval)
atIndex:index++];
[_refreshField setText:@"0.5"];
[self updateRefreshInterval];
}
- (void)registerForNotifications {
// On iOS 7, the screen coordinate system is not dependent on orientation so
// the debugger has to handle its own rotation.
if (!base::ios::IsRunningOnIOS8OrLater()) {
// Register to receive orientation notifications.
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(adjustForOrientation:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
}
// Register to receive memory warning.
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(lowMemoryWarningReceived:)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
// Register to receive keyboard will show notification.
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
// Register to receive keyboard will hide notification.
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
}
// Adds subviews for the specified metric, the value of which will be displayed
// in |label|.
- (void)addMetricWithName:(NSString*)name
atIndex:(NSUInteger)index
usingLabel:(UILabel*)label {
// The width of the view for the metric's name.
const CGFloat kNameWidth = 150;
// The width of the view for each metric.
const CGFloat kMetricWidth = 100;
CGPoint nameOrigin = [self originForSubviewAtIndex:index];
CGRect nameFrame =
CGRectMake(nameOrigin.x, nameOrigin.y, kNameWidth, [_font lineHeight]);
base::scoped_nsobject<UILabel> nameLabel(
[[UILabel alloc] initWithFrame:nameFrame]);
[nameLabel setText:[NSString stringWithFormat:@"%@: ", name]];
[nameLabel setFont:_font];
[self addSubview:nameLabel];
label.frame = CGRectMake(CGRectGetMaxX(nameFrame), nameFrame.origin.y,
kMetricWidth, [_font lineHeight]);
[label setFont:_font];
[label setTextAlignment:NSTextAlignmentRight];
[self addSubview:label];
}
// Adds a subview for a button with the given title and target/action.
- (void)addButtonWithTitle:(NSString*)title
target:(id)target
action:(SEL)action
withOrigin:(CGPoint)origin {
base::scoped_nsobject<UIButton> button(
[[UIButton buttonWithType:UIButtonTypeSystem] retain]);
[button setTitle:title forState:UIControlStateNormal];
[button titleLabel].font = _font;
[[button titleLabel] setTextAlignment:NSTextAlignmentCenter];
[button sizeToFit];
[button setFrame:CGRectMake(origin.x, origin.y, [button frame].size.width,
[_font lineHeight])];
[button addTarget:target
action:action
forControlEvents:UIControlEventTouchUpInside];
[self addSubview:button];
}
// Adds subviews for a UI component with label and input text field.
//
// -------------------------
// | labelText | <input> |
// -------------------------
//
// The inputTarget/inputAction will be invoked when the user finishes editing
// in |input|.
- (void)addLabelWithText:(NSString*)labelText
input:(UITextField*)input
inputTarget:(id)inputTarget
inputAction:(SEL)inputAction
atIndex:(NSUInteger)index {
[self addLabelWithText:labelText
input:input
inputTarget:inputTarget
inputAction:inputAction
buttonWithTitle:nil
buttonTarget:nil
buttonAction:nil
atIndex:index];
}
// Adds subviews for a UI component with label, input text field and button.
//
// -------------------------------------
// | labelText | <input> | <button> |
// -------------------------------------
//
// The inputTarget/inputAction will be invoked when the user finishes editing
// in |input|.
- (void)addLabelWithText:(NSString*)labelText
input:(UITextField*)input
inputTarget:(id)inputTarget
inputAction:(SEL)inputAction
buttonWithTitle:(NSString*)buttonTitle
buttonTarget:(id)buttonTarget
buttonAction:(SEL)buttonAction
atIndex:(NSUInteger)index {
base::scoped_nsobject<UILabel> label(
[[UILabel alloc] initWithFrame:CGRectZero]);
if (labelText) {
[label setText:[NSString stringWithFormat:@"%@: ", labelText]];
}
[label setFont:_font];
[label sizeToFit];
CGPoint labelOrigin = [self originForSubviewAtIndex:index];
[label setFrame:CGRectOffset([label frame], labelOrigin.x, labelOrigin.y)];
[self addSubview:label];
if (input) {
// The width of the views for each input text field.
const CGFloat kInputWidth = 50;
input.frame =
CGRectMake(CGRectGetMaxX([label frame]) + kPadding,
[label frame].origin.y, kInputWidth, [_font lineHeight]);
input.font = _font;
input.backgroundColor = [UIColor whiteColor];
input.delegate = self;
input.keyboardType = UIKeyboardTypeNumbersAndPunctuation;
input.adjustsFontSizeToFitWidth = YES;
input.textAlignment = NSTextAlignmentRight;
[input addTarget:inputTarget
action:inputAction
forControlEvents:UIControlEventEditingDidEnd];
[self addSubview:input];
}
if (buttonTitle) {
const CGFloat kButtonXOffset =
input ? CGRectGetMaxX(input.frame) : CGRectGetMaxX([label frame]);
CGPoint origin =
CGPointMake(kButtonXOffset + kPadding, [label frame].origin.y);
[self addButtonWithTitle:buttonTitle
target:buttonTarget
action:buttonAction
withOrigin:origin];
}
}
// Returns the CGPoint of the origin of the subview at |index|.
- (CGPoint)originForSubviewAtIndex:(NSUInteger)index {
return CGPointMake(kPadding,
(index + 1) * kPadding + index * [_font lineHeight]);
}
#pragma mark Refresh callback
// Updates content and ensures the view is visible.
- (void)refresh:(NSTimer*)timer {
[self.superview bringSubviewToFront:self];
[self updateMemoryInfo];
}
#pragma mark Memory inspection
// Updates the memory metrics shown.
- (void)updateMemoryInfo {
CGFloat value = memory_util::GetFreePhysicalBytes() / kNumBytesInMB;
[_physicalFreeMemoryLabel
setText:[NSString stringWithFormat:@"%.2f MB", value]];
value = memory_util::GetRealMemoryUsedInBytes() / kNumBytesInMB;
[_realMemoryUsedLabel setText:[NSString stringWithFormat:@"%.2f MB", value]];
value = memory_util::GetInternalVMBytes() / kNumBytesInMB;
[_xcodeGaugeLabel setText:[NSString stringWithFormat:@"%.2f MB", value]];
value = memory_util::GetDirtyVMBytes() / kNumBytesInMB;
[_dirtyVirtualMemoryLabel
setText:[NSString stringWithFormat:@"%.2f MB", value]];
}
#pragma mark Memory Warning notification callback
// Flashes the debugger to indicate memory warning.
- (void)lowMemoryWarningReceived:(NSNotification*)notification {
UIColor* originalColor = self.backgroundColor;
self.backgroundColor =
[UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:0.9];
[UIView animateWithDuration:1.0
delay:0.0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
self.backgroundColor = originalColor;
}
completion:nil];
}
#pragma mark Rotation notification callback
- (void)didMoveToSuperview {
UIView* superview = [self superview];
if (superview)
[self setCenter:[superview center]];
}
- (void)adjustForOrientation:(NSNotification*)notification {
if (base::ios::IsRunningOnIOS8OrLater()) {
return;
}
UIInterfaceOrientation orientation =
[[UIApplication sharedApplication] statusBarOrientation];
if (orientation == _currentOrientation) {
return;
}
_currentOrientation = orientation;
CGFloat angle;
switch (orientation) {
case UIInterfaceOrientationPortrait:
angle = 0;
break;
case UIInterfaceOrientationPortraitUpsideDown:
angle = M_PI;
break;
case UIInterfaceOrientationLandscapeLeft:
angle = -M_PI_2;
break;
case UIInterfaceOrientationLandscapeRight:
angle = M_PI_2;
break;
case UIInterfaceOrientationUnknown:
default:
angle = 0;
}
// Since the debugger view is in screen coordinates and handles its own
// rotation via the |transform| property, the view's position after rotation
// can be unexpected and partially off-screen. Centering the view before
// rotating it ensures that the view remains within the bounds of the screen.
if (self.superview) {
self.center = self.superview.center;
}
self.transform = CGAffineTransformMakeRotation(angle);
}
#pragma mark Keyboard notification callbacks
// Ensures the debugger is visible by shifting it up as the keyboard animates
// in.
- (void)keyboardWillShow:(NSNotification*)notification {
NSDictionary* userInfo = [notification userInfo];
NSValue* keyboardFrameValue =
[userInfo valueForKey:UIKeyboardFrameEndUserInfoKey];
CGFloat keyboardHeight = CurrentKeyboardHeight(keyboardFrameValue);
// Get the coord of the bottom of the debugger's frame. This is orientation
// dependent on iOS 7 because the debugger is in screen coords.
CGFloat bottomOfFrame = CGRectGetMaxY(self.frame);
if (!base::ios::IsRunningOnIOS8OrLater() && IsLandscape())
bottomOfFrame = CGRectGetMaxX(self.frame);
// Shift the debugger up by the "height" of the keyboard, but since the
// keyboard rect is in screen coords, use the orientation to find the height.
CGFloat distanceFromBottom = CurrentScreenHeight() - bottomOfFrame;
_keyboardOffset = -1 * fmax(0.0f, keyboardHeight - distanceFromBottom);
[self animateForKeyboardNotification:notification
withOffset:CGPointMake(0, _keyboardOffset)];
}
// Shifts the debugger back down when the keyboard is hidden.
- (void)keyboardWillHide:(NSNotification*)notification {
[self animateForKeyboardNotification:notification
withOffset:CGPointMake(0, -_keyboardOffset)];
}
- (void)animateForKeyboardNotification:(NSNotification*)notification
withOffset:(CGPoint)offset {
// Account for orientation.
offset = CGPointApplyAffineTransform(offset, self.transform);
// Normally this would use an animation block, but there is no API to
// convert the UIKeyboardAnimationCurveUserInfoKey's value from a
// UIViewAnimationCurve to a UIViewAnimationOption. Awesome!
NSDictionary* userInfo = [notification userInfo];
[UIView beginAnimations:nil context:nullptr];
[UIView setAnimationDuration:
[userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
NSInteger animationCurveKeyValue =
[userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
UIViewAnimationCurve animationCurve =
(UIViewAnimationCurve)animationCurveKeyValue;
[UIView setAnimationCurve:animationCurve];
[UIView setAnimationBeginsFromCurrentState:YES];
self.frame = CGRectOffset(self.frame, offset.x, offset.y);
[UIView commitAnimations];
}
#pragma mark Artificial memory bloat methods
- (void)updateBloat {
double bloatSizeMB;
NSScanner* scanner = [NSScanner scannerWithString:[_bloatField text]];
if (![scanner scanDouble:&bloatSizeMB] || bloatSizeMB < 0.0) {
bloatSizeMB = 0;
NSString* errorMessage =
[NSString stringWithFormat:@"Invalid value \"%@\" for bloat size.\n"
@"Must be a positive number.\n"
@"Resetting to %.1f MB",
[_bloatField text], bloatSizeMB];
[self alert:errorMessage];
[_bloatField setText:[NSString stringWithFormat:@"%.1f", bloatSizeMB]];
}
const CGFloat kBloatSizeBytes = ceil(bloatSizeMB * kNumBytesInMB);
const uint64 kNumberOfBytes = static_cast<uint64>(kBloatSizeBytes);
_bloat.reset(kNumberOfBytes ? new uint8[kNumberOfBytes] : nullptr);
if (_bloat) {
memset(_bloat.get(), -1, kNumberOfBytes); // Occupy memory.
} else {
if (kNumberOfBytes) {
[self alert:@"Could not allocate memory."];
}
}
}
- (void)clearBloat {
[_bloatField setText:@"0"];
[_bloatField resignFirstResponder];
[self updateBloat];
}
#pragma mark Refresh interval methods
- (void)updateRefreshInterval {
double refreshTimerValue;
NSScanner* scanner = [NSScanner scannerWithString:[_refreshField text]];
if (![scanner scanDouble:&refreshTimerValue] || refreshTimerValue < 0.0) {
refreshTimerValue = 0.5;
NSString* errorMessage = [NSString
stringWithFormat:@"Invalid value \"%@\" for refresh interval.\n"
@"Must be a positive number.\n" @"Resetting to %.1f",
[_refreshField text], refreshTimerValue];
[self alert:errorMessage];
[_refreshField
setText:[NSString stringWithFormat:@"%.1f", refreshTimerValue]];
return;
}
[_refreshTimer invalidate];
_refreshTimer.reset(
[[NSTimer scheduledTimerWithTimeInterval:refreshTimerValue
target:self
selector:@selector(refresh:)
userInfo:nil
repeats:YES] retain]);
}
#pragma mark Memory warning interval methods
// Since _performMemoryWarning is a private API it can't be compiled into
// official builds.
// TODO(lliabraa): Figure out how to support memory warnings (or something
// like them) in official builds.
#if CHROMIUM_BUILD
- (void)updateMemoryWarningInterval {
[_memoryWarningTimer invalidate];
double timerValue;
NSString* text = [_continuousMemoryWarningField text];
NSScanner* scanner = [NSScanner scannerWithString:text];
BOOL valueFound = [scanner scanDouble:&timerValue];
// If the text field is empty or contains 0, return early to turn off
// continuous memory warnings.
if (![text length] || timerValue == 0.0) {
return;
}
// If no value could be parsed or a non-positive value was found, throw up an
// error message and return early to turn off continuous memory warnings.
if (!valueFound || timerValue <= 0.0) {
NSString* errorMessage = [NSString
stringWithFormat:@"Invalid value \"%@\" for memory warning interval.\n"
@"Must be a positive number.\n"
@"Turning off continuous memory warnings",
text];
[self alert:errorMessage];
[_continuousMemoryWarningField setText:@""];
return;
}
// If a valid value was found have the timer start triggering continuous
// memory warnings.
_memoryWarningTimer.reset(
[[NSTimer scheduledTimerWithTimeInterval:timerValue
target:[UIApplication sharedApplication]
selector:@selector(_performMemoryWarning)
userInfo:nil
repeats:YES] retain]);
}
#endif // CHROMIUM_BUILD
#pragma mark UITextViewDelegate methods
// Dismisses the keyboard if the user hits return.
- (BOOL)textFieldShouldReturn:(UITextField*)textField {
[textField resignFirstResponder];
return YES;
}
#pragma mark UIResponder methods
// Allows the debugger to be dragged around the screen.
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
UITouch* touch = [touches anyObject];
CGPoint start = [touch previousLocationInView:self];
CGPoint end = [touch locationInView:self];
CGPoint offset = CGPointMake(end.x - start.x, end.y - start.y);
offset = CGPointApplyAffineTransform(offset, self.transform);
self.frame = CGRectOffset(self.frame, offset.x, offset.y);
}
#pragma mark Error handling
// Shows an alert with the given |errorMessage|.
- (void)alert:(NSString*)errorMessage {
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"Error"
message:errorMessage
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil, nil];
[alert show];
}
@end
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef IOS_CHROME_BROWSER_MEMORY_MEMORY_DEBUGGER_MANAGER_H_
#define IOS_CHROME_BROWSER_MEMORY_MEMORY_DEBUGGER_MANAGER_H_
#import <Foundation/Foundation.h>
class PrefRegistrySimple;
class PrefService;
@class UIView;
// A class to manage the life cycle of a MemoryDebugger instance.
//
// A MemoryDebugger's existence is controlled by a pref in local state, so the
// MemoryDebuggerManager listens for changes to that pref and instantiates or
// frees the debugger as appropriate.
@interface MemoryDebuggerManager : NSObject
// Designated initializer.
- (instancetype)initWithView:(UIView*)view prefs:(PrefService*)prefs;
// Registers local state preferences.
+ (void)registerLocalState:(PrefRegistrySimple*)registry;
@end
#endif // IOS_CHROME_BROWSER_MEMORY_MEMORY_DEBUGGER_MANAGER_H_
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/memory/memory_debugger_manager.h"
#include "base/ios/weak_nsobject.h"
#import "base/mac/bind_objc_block.h"
#include "base/mac/scoped_nsobject.h"
#include "base/prefs/pref_member.h"
#include "base/prefs/pref_registry_simple.h"
#include "base/prefs/pref_service.h"
#import "ios/chrome/browser/memory/memory_debugger.h"
#import "ios/chrome/browser/pref_names.h"
@implementation MemoryDebuggerManager {
UIView* debuggerParentView_; // weak
base::scoped_nsobject<MemoryDebugger> memoryDebugger_;
BooleanPrefMember showMemoryDebugger_;
}
- (instancetype)initWithView:(UIView*)debuggerParentView
prefs:(PrefService*)prefs {
if (self = [super init]) {
debuggerParentView_ = debuggerParentView;
// Set up the callback for when the pref to show/hide the debugger changes.
base::WeakNSObject<MemoryDebuggerManager> weakSelf(self);
base::Closure callback = base::BindBlock(^{
base::scoped_nsobject<MemoryDebuggerManager> strongSelf(
[weakSelf retain]);
if (strongSelf) {
[self onShowMemoryDebuggingToolsChange];
}
});
showMemoryDebugger_.Init(prefs::kShowMemoryDebuggingTools, prefs, callback);
// Invoke the pref change callback once to show the debugger on start up,
// if necessary.
[self onShowMemoryDebuggingToolsChange];
}
return self;
}
- (void)dealloc {
[self tearDownDebugger];
[super dealloc];
}
#pragma mark - Pref-handling methods
// Registers local state prefs.
+ (void)registerLocalState:(PrefRegistrySimple*)registry {
registry->RegisterBooleanPref(prefs::kShowMemoryDebuggingTools, false);
}
// Shows or hides the debugger when the pref changes.
- (void)onShowMemoryDebuggingToolsChange {
if (showMemoryDebugger_.GetValue()) {
memoryDebugger_.reset([[MemoryDebugger alloc] init]);
[debuggerParentView_ addSubview:memoryDebugger_];
} else {
[self tearDownDebugger];
}
}
// Tears down the debugger so it can be deallocated.
- (void)tearDownDebugger {
[memoryDebugger_ invalidateTimers];
[memoryDebugger_ removeFromSuperview];
memoryDebugger_.reset();
}
@end
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ios/chrome/browser/memory/memory_metrics.h"
#include <mach/mach.h>
#include "base/logging.h"
#include "base/process/process_handle.h"
#include "base/process/process_metrics.h"
#ifdef ARCH_CPU_64_BITS
#define cr_vm_region vm_region_64
#else
#define cr_vm_region vm_region
#endif
namespace {
// The number of pages returned by host_statistics and vm_region are a count
// of pages of 4096 bytes even when running on arm64 but the constants that
// are exposed (vm_page_size, VM_PAGE_SIZE, host_page_size) are all equals to
// 16384 bytes. So we define our own constant here to convert from page count
// to bytes.
const uint64_t kVMPageSize = 4096;
}
namespace memory_util {
uint64 GetFreePhysicalBytes() {
vm_statistics_data_t vmstat;
mach_msg_type_number_t count = HOST_VM_INFO_COUNT;
kern_return_t result =
host_statistics(mach_host_self(), HOST_VM_INFO,
reinterpret_cast<host_info_t>(&vmstat), &count);
if (result != KERN_SUCCESS) {
LOG(ERROR) << "Calling host_statistics failed.";
return 0;
}
return vmstat.free_count * kVMPageSize;
}
uint64 GetRealMemoryUsedInBytes() {
base::ProcessHandle process_handle = base::GetCurrentProcessHandle();
scoped_ptr<base::ProcessMetrics> process_metrics(
base::ProcessMetrics::CreateProcessMetrics(process_handle));
return static_cast<uint64>(process_metrics->GetWorkingSetSize());
}
uint64 GetDirtyVMBytes() {
// Iterate over all VM regions and sum their dirty pages.
unsigned int total_dirty_pages = 0;
vm_size_t vm_size = 0;
kern_return_t result;
for (vm_address_t address = MACH_VM_MIN_ADDRESS;; address += vm_size) {
vm_region_extended_info_data_t info;
mach_msg_type_number_t info_count = VM_REGION_EXTENDED_INFO_COUNT;
mach_port_t object_name;
result = cr_vm_region(
mach_task_self(), &address, &vm_size, VM_REGION_EXTENDED_INFO,
reinterpret_cast<vm_region_info_t>(&info), &info_count, &object_name);
if (result == KERN_INVALID_ADDRESS) {
// The end of the address space has been reached.
break;
} else if (result != KERN_SUCCESS) {
LOG(ERROR) << "Calling vm_region failed with code: " << result;
break;
} else {
total_dirty_pages += info.pages_dirtied;
}
}
return total_dirty_pages * kVMPageSize;
}
uint64 GetInternalVMBytes() {
task_vm_info_data_t task_vm_info;
mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
kern_return_t result =
task_info(mach_task_self(), TASK_VM_INFO,
reinterpret_cast<task_info_t>(&task_vm_info), &count);
if (result != KERN_SUCCESS) {
LOG(ERROR) << "Calling task_info failed.";
return 0;
}
return static_cast<uint64>(task_vm_info.internal);
}
} // namespace memory_util
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef IOS_CHROME_BROWSER_MEMORY_MEMORY_METRICS_H_
#define IOS_CHROME_BROWSER_MEMORY_MEMORY_METRICS_H_
#include "base/basictypes.h"
namespace memory_util {
// "Physical Free" memory metric. This corresponds to the "Physical Memory Free"
// value reported by the Memory Monitor in Instruments.
uint64 GetFreePhysicalBytes();
// "Real Memory Used" memory metric. This corresponds to the "Real Memory" value
// reported for the app by the Memory Monitor in Instruments.
uint64 GetRealMemoryUsedInBytes();
// "Xcode Gauge" memory metric. This corresponds to the "Memory" value reported
// for the app by the Debug Navigator in Xcode. Only supported in iOS 7 and
// later.
uint64 GetInternalVMBytes();
// "Dirty VM" memory metric. This corresponds to the "Dirty Size" value reported
// for the app by the VM Tracker in Instruments.
uint64 GetDirtyVMBytes();
} // namespace memory_util
#endif // IOS_CHROME_BROWSER_MEMORY_MEMORY_METRICS_H_
......@@ -33,4 +33,8 @@ const char kIosBookmarkPromoAlreadySeen[] = "ios.bookmark.promo_already_seen";
// The preferred SSO user for wallet payments.
const char kPaymentsPreferredUserId[] = "ios.payments.preferred_user_id";
// True if the memory debugging tools should be visible.
extern const char kShowMemoryDebuggingTools[] =
"ios.memory.show_debugging_tools";
} // namespace prefs
......@@ -20,6 +20,7 @@ namespace prefs {
extern const char kIosBookmarkFolderDefault[];
extern const char kIosBookmarkPromoAlreadySeen[];
extern const char kPaymentsPreferredUserId[];
extern const char kShowMemoryDebuggingTools[];
} // namespace prefs
......
......@@ -105,6 +105,12 @@
'browser/infobars/infobar_manager_impl.h',
'browser/infobars/infobar_utils.h',
'browser/infobars/infobar_utils.mm',
'browser/memory/memory_debugger.h',
'browser/memory/memory_debugger.mm',
'browser/memory/memory_debugger_manager.h',
'browser/memory/memory_debugger_manager.mm',
'browser/memory/memory_metrics.cc',
'browser/memory/memory_metrics.h',
'browser/net/chrome_cookie_store_ios_client.h',
'browser/net/chrome_cookie_store_ios_client.mm',
'browser/net/image_fetcher.h',
......
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