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