mmap.js revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
1// Copyright 2014 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 5mmap = new (function() { 6 7this.COL_START = 0; 8this.COL_END = 1; 9this.COL_LEN = 2; 10this.COL_PROT = 3; 11this.COL_RSS = 4; 12this.COL_PRIV_DIRTY = 5; 13this.COL_PRIV_CLEAN = 6; 14this.COL_SHARED_DIRTY = 7; 15this.COL_SHARED_CLEAN = 8; 16this.COL_FILE = 9; 17this.COL_OFFSET = 10; 18this.COL_RESIDENT = 11; 19this.SHOW_COLUMNS = [this.COL_START, this.COL_END, this.COL_LEN, this.COL_PROT, 20 this.COL_RSS, this.COL_PRIV_DIRTY, this.COL_PRIV_CLEAN, 21 this.COL_SHARED_DIRTY, this.COL_SHARED_CLEAN, this.COL_FILE]; 22 23this.PAGE_SIZE = 4096; 24 25this.mapsData_ = null; 26this.mapsTable_ = null; 27this.mapsFilter_ = null; 28this.shouldUpdateProfileAfterDump_ = false; 29 30this.onDomReady_ = function() { 31 $('#mm-lookup-addr').on('change', this.lookupAddress.bind(this)); 32 $('#mm-filter-file').on('change', this.applyMapsTableFilters_.bind(this)); 33 $('#mm-filter-prot').on('change', this.applyMapsTableFilters_.bind(this)); 34 $('#mm-filter-clear').on('click', this.resetMapsTableFilters_.bind(this)); 35 36 // Create the mmaps table. 37 this.mapsTable_ = new google.visualization.Table($('#mm-table')[0]); 38 google.visualization.events.addListener( 39 this.mapsTable_, 'select', this.onMmapTableRowSelect_.bind(this)); 40 $('#mm-table').on('dblclick', this.onMmapTableDblClick_.bind(this)); 41}; 42 43this.dumpMmaps = function(targetProcUri, updateProfile) { 44 if (!targetProcUri) 45 return; 46 this.shouldUpdateProfileAfterDump_ = !!updateProfile; 47 webservice.ajaxRequest('/dump/mmap/' + targetProcUri, 48 this.onDumpAjaxResponse_.bind(this)); 49 rootUi.showDialog('Dumping memory maps for ' + targetProcUri + '...'); 50}; 51 52this.dumpMmapsFromStorage = function(archiveName, snapshot) { 53 webservice.ajaxRequest('/storage/' + archiveName + '/' + snapshot + '/mmaps', 54 this.onDumpAjaxResponse_.bind(this)); 55 rootUi.showDialog('Loading memory maps from archive ...'); 56}; 57 58this.onDumpAjaxResponse_ = function(data) { 59 $('#mm-filter-file').val(''); 60 $('#mm-filter-prot').val(''); 61 this.mapsData_ = new google.visualization.DataTable(data.table); 62 this.mapsFilter_ = new google.visualization.DataView(this.mapsData_); 63 this.mapsFilter_.setColumns(this.SHOW_COLUMNS); 64 this.applyMapsTableFilters_(); 65 rootUi.hideDialog(); 66 if (this.shouldUpdateProfileAfterDump_) 67 profiler.profileCachedMmapDump(data.id); 68 shouldUpdateProfileAfterDump_ = false; 69}; 70 71this.applyMapsTableFilters_ = function() { 72 // Filters the rows according to the user-provided file and prot regexps. 73 if (!this.mapsFilter_) 74 return; 75 76 var fileRx = $('#mm-filter-file').val(); 77 var protRx = $('#mm-filter-prot').val(); 78 var rows = []; 79 var totPrivDirty = 0; 80 var totPrivClean = 0; 81 var totSharedDirty = 0; 82 var totSharedClean = 0; 83 84 for (var row = 0; row < this.mapsData_.getNumberOfRows(); ++row) { 85 mappedFile = this.mapsData_.getValue(row, this.COL_FILE); 86 protFlags = this.mapsData_.getValue(row, this.COL_PROT); 87 if (!mappedFile.match(fileRx) || !protFlags.match(protRx)) 88 continue; 89 rows.push(row); 90 totPrivDirty += this.mapsData_.getValue(row, this.COL_PRIV_DIRTY); 91 totPrivClean += this.mapsData_.getValue(row, this.COL_PRIV_CLEAN); 92 totSharedDirty += this.mapsData_.getValue(row,this.COL_SHARED_DIRTY); 93 totSharedClean += this.mapsData_.getValue(row, this.COL_SHARED_CLEAN); 94 } 95 96 this.mapsFilter_.setRows(rows); 97 $('#mm-totals-priv-dirty').text(totPrivDirty); 98 $('#mm-totals-priv-clean').text(totPrivClean); 99 $('#mm-totals-shared-dirty').text(totSharedDirty); 100 $('#mm-totals-shared-clean').text(totSharedClean); 101 this.redraw(); 102}; 103 104this.resetMapsTableFilters_ = function() { 105 $('#mm-filter-file').val(''); 106 $('#mm-filter-prot').val(''); 107 this.applyMapsTableFilters_(); 108}; 109 110this.onMmapTableRowSelect_ = function() { 111 // Update the memory totals for the selected rows. 112 if (!this.mapsFilter_) 113 return; 114 115 var totPrivDirty = 0; 116 var totPrivClean = 0; 117 var totSharedDirty = 0; 118 var totSharedClean = 0; 119 120 this.mapsTable_.getSelection().forEach(function(sel) { 121 var row = sel.row; 122 totPrivDirty += this.mapsFilter_.getValue(row, this.COL_PRIV_DIRTY); 123 totPrivClean += this.mapsFilter_.getValue(row, this.COL_PRIV_CLEAN); 124 totSharedDirty += this.mapsFilter_.getValue(row,this.COL_SHARED_DIRTY); 125 totSharedClean += this.mapsFilter_.getValue(row, this.COL_SHARED_CLEAN); 126 }, this); 127 $('#mm-selected-priv-dirty').text(totPrivDirty); 128 $('#mm-selected-priv-clean').text(totPrivClean); 129 $('#mm-selected-shared-dirty').text(totSharedDirty); 130 $('#mm-selected-shared-clean').text(totSharedClean); 131}; 132 133this.onMmapTableDblClick_ = function() { 134 // Show resident pages for the selected mapping. 135 var PAGES_PER_ROW = 16; 136 137 if (!this.mapsData_) 138 return; 139 140 var sel = this.mapsTable_.getSelection(); 141 if (sel.length == 0) 142 return; 143 144 // |sel| returns the row index in the current view, which might be filtered. 145 // Need to walk back in the mapsFilter_.getViewRows to get the actual row 146 // index in the original table. 147 var row = this.mapsFilter_.getViewRows()[sel[0].row]; 148 var arr = JSON.parse(this.mapsData_.getValue(row, this.COL_RESIDENT)); 149 var table = $('<table class="mm-resident-table"/>'); 150 var curRow = $('<tr/>'); 151 table.append(curRow); 152 153 for (var i = 0; i < arr.length; ++i) { 154 for (var j = 0; j < 8; ++j) { 155 var pageIdx = i * 8 + j; 156 var resident = !!(arr[i] & (1 << j)); 157 if (pageIdx % PAGES_PER_ROW == 0) { 158 curRow = $('<tr/>'); 159 table.append(curRow); 160 } 161 var hexAddr = (pageIdx * this.PAGE_SIZE).toString(16); 162 var cell = $('<td/>').text(hexAddr); 163 if (resident) 164 cell.addClass('resident') 165 curRow.append(cell); 166 } 167 } 168 rootUi.showDialog(table, 'Resident page list'); 169}; 170 171this.redraw = function() { 172 if (!this.mapsFilter_) 173 return; 174 this.mapsTable_.draw(this.mapsFilter_); 175}; 176 177this.lookupAddress = function() { 178 // Looks up the user-provided address in the mmap table and highlights the 179 // row containing the map (if found). 180 if (!this.mapsData_) 181 return; 182 183 addr = parseInt($('#mm-lookup-addr').val(), 16); 184 $('#mm-lookup-offset').val(''); 185 if (!addr) 186 return; 187 188 this.resetMapsTableFilters_(); 189 190 var lbound = 0; 191 var ubound = this.mapsData_.getNumberOfRows() - 1; 192 while (lbound <= ubound) { 193 var row = ((lbound + ubound) / 2) >> 0; 194 var start = parseInt(this.mapsData_.getValue(row, this.COL_START), 16); 195 var end = parseInt(this.mapsData_.getValue(row, this.COL_END), 16); 196 if (addr < start){ 197 ubound = row - 1; 198 } 199 else if (addr > end) { 200 lbound = row + 1; 201 } 202 else { 203 $('#mm-lookup-offset').val((addr - start).toString(16)); 204 this.mapsTable_.setSelection([{row: row, column: null}]); 205 // Scroll to row. 206 $('#wrapper').scrollTop( 207 $('#mm-table .google-visualization-table-tr-sel').offset().top); 208 break; 209 } 210 } 211}; 212 213$(document).ready(this.onDomReady_.bind(this)); 214 215})();