2015-09-26 06:09:58 +03:00
/ * T h i s S o u r c e C o d e F o r m i s s u b j e c t t o t h e t e r m s o f t h e M o z i l l a P u b l i c
* License , v . 2.0 . If a copy of the MPL was not distributed with this file ,
* You can obtain one at http : //mozilla.org/MPL/2.0/. */
"use strict" ;
2016-04-12 04:04:31 +03:00
const { Preferences } = require ( "resource://gre/modules/Preferences.jsm" ) ;
const { assert , reportException , isSet } = require ( "devtools/shared/DevToolsUtils" ) ;
2016-01-13 23:27:30 +03:00
const {
censusIsUpToDate ,
getSnapshot ,
createSnapshot ,
dominatorTreeIsComputed ,
} = require ( "../utils" ) ;
2016-04-01 17:56:00 +03:00
const {
actions ,
snapshotState : states ,
viewState ,
censusState ,
treeMapState ,
2016-04-12 04:04:31 +03:00
dominatorTreeState ,
individualsState ,
2016-04-01 17:56:00 +03:00
} = require ( "../constants" ) ;
2016-02-03 17:17:00 +03:00
const telemetry = require ( "../telemetry" ) ;
2016-01-13 23:27:30 +03:00
const view = require ( "./view" ) ;
const refresh = require ( "./refresh" ) ;
2016-03-07 23:23:32 +03:00
const diffing = require ( "./diffing" ) ;
2016-04-12 04:04:31 +03:00
const TaskCache = require ( "./task-cache" ) ;
2015-10-15 09:13:17 +03:00
/ * *
2015-11-13 22:20:45 +03:00
* A series of actions are fired from this task to save , read and generate the
* initial census from a snapshot .
2015-10-15 09:13:17 +03:00
*
* @ param { MemoryFront }
* @ param { HeapAnalysesClient }
* @ param { Object }
* /
2015-10-17 05:15:54 +03:00
const takeSnapshotAndCensus = exports . takeSnapshotAndCensus = function ( front , heapWorker ) {
2016-05-17 21:25:54 +03:00
return function * ( dispatch , getState ) {
2015-11-13 22:20:45 +03:00
const id = yield dispatch ( takeSnapshot ( front ) ) ;
if ( id === null ) {
return ;
}
2015-10-27 23:24:55 +03:00
2015-11-13 22:20:45 +03:00
yield dispatch ( readSnapshot ( heapWorker , id ) ) ;
2016-04-05 20:21:00 +03:00
if ( getSnapshot ( getState ( ) , id ) . state !== states . READ ) {
return ;
}
2016-03-21 19:14:16 +03:00
yield dispatch ( computeSnapshotData ( heapWorker , id ) ) ;
} ;
} ;
/ * *
* Create the census for the snapshot with the provided snapshot id . If the
* current view is the DOMINATOR _TREE view , create the dominator tree for this
* snapshot as well .
*
* @ param { HeapAnalysesClient } heapWorker
* @ param { snapshotId } id
* /
2016-05-17 21:25:54 +03:00
const computeSnapshotData = exports . computeSnapshotData = function ( heapWorker , id ) {
return function * ( dispatch , getState ) {
2016-03-21 19:14:16 +03:00
if ( getSnapshot ( getState ( ) , id ) . state !== states . READ ) {
return ;
}
2016-01-13 23:27:30 +03:00
2016-04-01 17:56:00 +03:00
// Decide which type of census to take.
2016-04-12 04:04:31 +03:00
const censusTaker = getCurrentCensusTaker ( getState ( ) . view . state ) ;
2016-04-01 17:56:00 +03:00
yield dispatch ( censusTaker ( heapWorker , id ) ) ;
2016-04-12 04:04:31 +03:00
if ( getState ( ) . view . state === viewState . DOMINATOR _TREE &&
2016-04-05 20:21:00 +03:00
! getSnapshot ( getState ( ) , id ) . dominatorTree ) {
2016-03-21 19:14:16 +03:00
yield dispatch ( computeAndFetchDominatorTree ( heapWorker , id ) ) ;
2015-10-27 23:24:55 +03:00
}
2015-10-15 09:13:17 +03:00
} ;
} ;
2015-09-26 06:09:58 +03:00
2015-10-17 05:15:54 +03:00
/ * *
* Selects a snapshot and if the snapshot ' s census is using a different
2016-03-02 03:20:45 +03:00
* display , take a new census .
2015-10-17 05:15:54 +03:00
*
2015-11-13 22:20:45 +03:00
* @ param { HeapAnalysesClient } heapWorker
* @ param { snapshotId } id
2015-10-17 05:15:54 +03:00
* /
2015-11-13 22:20:45 +03:00
const selectSnapshotAndRefresh = exports . selectSnapshotAndRefresh = function ( heapWorker , id ) {
2016-05-17 21:25:54 +03:00
return function * ( dispatch , getState ) {
2016-04-12 04:04:31 +03:00
if ( getState ( ) . diffing || getState ( ) . individuals ) {
2016-01-13 23:27:30 +03:00
dispatch ( view . changeView ( viewState . CENSUS ) ) ;
2015-11-13 22:20:45 +03:00
}
dispatch ( selectSnapshot ( id ) ) ;
2016-01-13 23:27:30 +03:00
yield dispatch ( refresh . refresh ( heapWorker ) ) ;
2015-10-17 05:15:54 +03:00
} ;
} ;
2015-10-10 18:09:28 +03:00
/ * *
2015-11-13 22:20:45 +03:00
* Take a snapshot and return its id on success , or null on failure .
*
* @ param { MemoryFront } front
* @ returns { Number | null }
2015-10-10 18:09:28 +03:00
* /
2015-10-17 05:15:54 +03:00
const takeSnapshot = exports . takeSnapshot = function ( front ) {
2016-05-17 21:25:54 +03:00
return function * ( dispatch , getState ) {
2016-02-03 17:17:00 +03:00
telemetry . countTakeSnapshot ( ) ;
2016-04-12 04:04:31 +03:00
if ( getState ( ) . diffing || getState ( ) . individuals ) {
2016-01-13 23:27:30 +03:00
dispatch ( view . changeView ( viewState . CENSUS ) ) ;
2015-11-13 22:20:45 +03:00
}
2016-01-13 23:27:30 +03:00
const snapshot = createSnapshot ( getState ( ) ) ;
2015-11-13 22:20:45 +03:00
const id = snapshot . id ;
2015-10-15 09:13:17 +03:00
dispatch ( { type : actions . TAKE _SNAPSHOT _START , snapshot } ) ;
2015-11-13 22:20:45 +03:00
dispatch ( selectSnapshot ( id ) ) ;
2015-10-15 09:13:17 +03:00
2015-10-27 23:24:55 +03:00
let path ;
try {
path = yield front . saveHeapSnapshot ( ) ;
} catch ( error ) {
reportException ( "takeSnapshot" , error ) ;
2015-11-13 22:20:45 +03:00
dispatch ( { type : actions . SNAPSHOT _ERROR , id , error } ) ;
return null ;
2015-10-27 23:24:55 +03:00
}
2015-10-15 09:13:17 +03:00
2015-11-13 22:20:45 +03:00
dispatch ( { type : actions . TAKE _SNAPSHOT _END , id , path } ) ;
return snapshot . id ;
2015-10-15 09:13:17 +03:00
} ;
} ;
/ * *
* Reads a snapshot into memory ; necessary to do before taking
* a census on the snapshot . May only be called once per snapshot .
*
2015-11-13 22:20:45 +03:00
* @ param { HeapAnalysesClient } heapWorker
* @ param { snapshotId } id
2015-10-15 09:13:17 +03:00
* /
2016-04-12 04:04:31 +03:00
const readSnapshot = exports . readSnapshot =
TaskCache . declareCacheableTask ( {
getCacheKey ( _ , id ) {
return id ;
} ,
2016-05-17 21:25:54 +03:00
task : function * ( heapWorker , id , removeFromCache , dispatch , getState ) {
2015-11-13 22:20:45 +03:00
const snapshot = getSnapshot ( getState ( ) , id ) ;
2015-11-11 20:52:36 +03:00
assert ( [ states . SAVED , states . IMPORTING ] . includes ( snapshot . state ) ,
2016-04-12 04:04:31 +03:00
` Should only read a snapshot once. Found snapshot in state ${ snapshot . state } ` ) ;
2015-10-15 09:13:17 +03:00
2015-10-29 07:27:46 +03:00
let creationTime ;
2015-11-13 22:20:45 +03:00
dispatch ( { type : actions . READ _SNAPSHOT _START , id } ) ;
2015-10-27 23:24:55 +03:00
try {
yield heapWorker . readHeapSnapshot ( snapshot . path ) ;
2015-10-29 07:27:46 +03:00
creationTime = yield heapWorker . getCreationTime ( snapshot . path ) ;
2015-10-27 23:24:55 +03:00
} catch ( error ) {
2016-04-12 04:04:31 +03:00
removeFromCache ( ) ;
2015-10-27 23:24:55 +03:00
reportException ( "readSnapshot" , error ) ;
2015-11-13 22:20:45 +03:00
dispatch ( { type : actions . SNAPSHOT _ERROR , id , error } ) ;
2015-10-27 23:24:55 +03:00
return ;
}
2016-04-12 04:04:31 +03:00
removeFromCache ( ) ;
2015-11-13 22:20:45 +03:00
dispatch ( { type : actions . READ _SNAPSHOT _END , id , creationTime } ) ;
2016-04-12 04:04:31 +03:00
}
} ) ;
let takeCensusTaskCounter = 0 ;
2015-10-15 09:13:17 +03:00
/ * *
2016-04-01 17:56:00 +03:00
* Census and tree maps both require snapshots . This function shares the logic
* of creating snapshots , but is configurable with specific actions for the
* individual census types .
2015-10-15 09:13:17 +03:00
*
2016-04-01 17:56:00 +03:00
* @ param { getDisplay } Get the display object from the state .
* @ param { getCensus } Get the census from the snapshot .
* @ param { beginAction } Action to send at the beginning of a heap snapshot .
* @ param { endAction } Action to send at the end of a heap snapshot .
* @ param { errorAction } Action to send if a snapshot has an error .
2015-10-15 09:13:17 +03:00
* /
2016-04-01 17:56:00 +03:00
function makeTakeCensusTask ( { getDisplay , getFilter , getCensus , beginAction ,
2016-04-05 20:21:00 +03:00
endAction , errorAction , canTakeCensus } ) {
2016-04-01 17:56:00 +03:00
/ * *
* @ param { HeapAnalysesClient } heapWorker
* @ param { snapshotId } id
*
* @ see { Snapshot } model defined in devtools / client / memory / models . js
* @ see ` devtools/shared/heapsnapshot/HeapAnalysesClient.js `
* @ see ` js/src/doc/Debugger/Debugger.Memory.md ` for breakdown details
* /
2016-04-12 04:04:31 +03:00
let thisTakeCensusTaskId = ++ takeCensusTaskCounter ;
return TaskCache . declareCacheableTask ( {
getCacheKey ( _ , id ) {
return ` take-census-task- ${ thisTakeCensusTaskId } - ${ id } ` ;
} ,
2016-05-17 21:25:54 +03:00
task : function * ( heapWorker , id , removeFromCache , dispatch , getState ) {
2016-04-01 17:56:00 +03:00
const snapshot = getSnapshot ( getState ( ) , id ) ;
2016-04-05 20:21:00 +03:00
if ( ! snapshot ) {
2016-04-12 04:04:31 +03:00
removeFromCache ( ) ;
2016-04-05 20:21:00 +03:00
return ;
}
2016-04-01 17:56:00 +03:00
2016-04-05 20:21:00 +03:00
// Assert that snapshot is in a valid state
2016-04-01 17:56:00 +03:00
assert ( canTakeCensus ( snapshot ) ,
2016-04-12 04:04:31 +03:00
` Attempting to take a census when the snapshot is not in a ready state. snapshot.state = ${ snapshot . state } , census.state = ${ ( getCensus ( snapshot ) || { state : null } ).state} ` ) ;
2016-04-01 17:56:00 +03:00
let report , parentMap ;
let display = getDisplay ( getState ( ) ) ;
let filter = getFilter ( getState ( ) ) ;
// If display, filter and inversion haven't changed, don't do anything.
if ( censusIsUpToDate ( filter , display , getCensus ( snapshot ) ) ) {
2016-04-12 04:04:31 +03:00
removeFromCache ( ) ;
2016-04-01 17:56:00 +03:00
return ;
}
2015-10-17 05:15:54 +03:00
2016-04-01 17:56:00 +03:00
// Keep taking a census if the display changes while our request is in
// flight. Recheck that the display used for the census is the same as the
// state's display.
do {
display = getDisplay ( getState ( ) ) ;
filter = getState ( ) . filter ;
dispatch ( {
type : beginAction ,
id ,
filter ,
display
} ) ;
2015-10-17 05:15:54 +03:00
2016-04-01 17:56:00 +03:00
let opts = display . inverted
? { asInvertedTreeNode : true }
: { asTreeNode : true } ;
opts . filter = filter || null ;
try {
( { report , parentMap } = yield heapWorker . takeCensus (
snapshot . path ,
{ breakdown : display . breakdown } ,
opts ) ) ;
} catch ( error ) {
2016-04-12 04:04:31 +03:00
removeFromCache ( ) ;
2016-04-01 17:56:00 +03:00
reportException ( "takeCensus" , error ) ;
dispatch ( { type : errorAction , id , error } ) ;
return ;
}
}
while ( filter !== getState ( ) . filter ||
display !== getDisplay ( getState ( ) ) ) ;
2015-11-05 03:12:31 +03:00
2016-04-12 04:04:31 +03:00
removeFromCache ( ) ;
2015-11-05 03:12:31 +03:00
dispatch ( {
2016-04-01 17:56:00 +03:00
type : endAction ,
2015-11-13 22:20:45 +03:00
id ,
2016-04-01 17:56:00 +03:00
display ,
2015-11-05 03:12:31 +03:00
filter ,
2016-04-01 17:56:00 +03:00
report ,
parentMap
2015-11-05 03:12:31 +03:00
} ) ;
2016-04-01 17:56:00 +03:00
telemetry . countCensus ( { filter , display } ) ;
2016-04-12 04:04:31 +03:00
}
} ) ;
2016-04-01 17:56:00 +03:00
}
2015-10-27 23:24:55 +03:00
2016-04-01 17:56:00 +03:00
/ * *
* Take a census .
* /
const takeCensus = exports . takeCensus = makeTakeCensusTask ( {
getDisplay : ( state ) => state . censusDisplay ,
getFilter : ( state ) => state . filter ,
getCensus : ( snapshot ) => snapshot . census ,
beginAction : actions . TAKE _CENSUS _START ,
endAction : actions . TAKE _CENSUS _END ,
2016-04-05 20:21:00 +03:00
errorAction : actions . TAKE _CENSUS _ERROR ,
canTakeCensus : snapshot =>
snapshot . state === states . READ &&
( ! snapshot . census || snapshot . census . state === censusState . SAVED ) ,
2016-04-01 17:56:00 +03:00
} ) ;
2015-10-15 09:13:17 +03:00
2016-04-01 17:56:00 +03:00
/ * *
* Take a census for the treemap .
* /
const takeTreeMap = exports . takeTreeMap = makeTakeCensusTask ( {
getDisplay : ( state ) => state . treeMapDisplay ,
getFilter : ( ) => null ,
getCensus : ( snapshot ) => snapshot . treeMap ,
beginAction : actions . TAKE _TREE _MAP _START ,
endAction : actions . TAKE _TREE _MAP _END ,
2016-04-05 20:21:00 +03:00
errorAction : actions . TAKE _TREE _MAP _ERROR ,
canTakeCensus : snapshot =>
snapshot . state === states . READ &&
( ! snapshot . treeMap || snapshot . treeMap . state === treeMapState . SAVED ) ,
2016-04-01 17:56:00 +03:00
} ) ;
2016-02-03 17:17:00 +03:00
2016-04-01 17:56:00 +03:00
/ * *
* Define what should be the default mode for taking a census based on the
* default view of the tool .
* /
const defaultCensusTaker = takeTreeMap ;
/ * *
* Pick the default census taker when taking a snapshot . This should be
* determined by the current view . If the view doesn ' t include a census , then
* use the default one defined above . Some census information is always needed
* to display some basic information about a snapshot .
*
* @ param { string } value from viewState
* /
2016-05-17 21:25:54 +03:00
const getCurrentCensusTaker = exports . getCurrentCensusTaker = function ( currentView ) {
2016-04-01 17:56:00 +03:00
switch ( currentView ) {
case viewState . TREE _MAP :
return takeTreeMap ;
case viewState . CENSUS :
return takeCensus ;
2016-04-08 19:36:34 +03:00
default :
return defaultCensusTaker ;
2016-04-01 17:56:00 +03:00
}
2015-10-27 19:11:05 +03:00
} ;
2016-04-12 04:04:31 +03:00
/ * *
* Focus the given node in the individuals view .
*
* @ param { DominatorTreeNode } node .
* /
2016-05-17 21:25:54 +03:00
const focusIndividual = exports . focusIndividual = function ( node ) {
2016-04-12 04:04:31 +03:00
return {
type : actions . FOCUS _INDIVIDUAL ,
node ,
} ;
} ;
/ * *
* Fetch the individual ` DominatorTreeNodes ` for the census group specified by
* ` censusBreakdown ` and ` reportLeafIndex ` .
*
* @ param { HeapAnalysesClient } heapWorker
* @ param { SnapshotId } id
* @ param { Object } censusBreakdown
* @ param { Set < Number > | Number } reportLeafIndex
* /
const fetchIndividuals = exports . fetchIndividuals =
2016-05-17 21:25:54 +03:00
function ( heapWorker , id , censusBreakdown , reportLeafIndex ) {
return function * ( dispatch , getState ) {
2016-04-12 04:04:31 +03:00
if ( getState ( ) . view . state !== viewState . INDIVIDUALS ) {
dispatch ( view . changeView ( viewState . INDIVIDUALS ) ) ;
}
const snapshot = getSnapshot ( getState ( ) , id ) ;
assert ( snapshot && snapshot . state === states . READ ,
"The snapshot should already be read into memory" ) ;
if ( ! dominatorTreeIsComputed ( snapshot ) ) {
yield dispatch ( computeAndFetchDominatorTree ( heapWorker , id ) ) ;
}
const snapshot _ = getSnapshot ( getState ( ) , id ) ;
assert ( snapshot _ . dominatorTree && snapshot _ . dominatorTree . root ,
"Should have a dominator tree with a root." ) ;
const dominatorTreeId = snapshot _ . dominatorTree . dominatorTreeId ;
const indices = isSet ( reportLeafIndex )
? reportLeafIndex
: new Set ( [ reportLeafIndex ] ) ;
let labelDisplay ;
let nodes ;
do {
labelDisplay = getState ( ) . labelDisplay ;
assert ( labelDisplay && labelDisplay . breakdown && labelDisplay . breakdown . by ,
` Should have a breakdown to label nodes with, got: ${ uneval ( labelDisplay ) } ` ) ;
if ( getState ( ) . view . state !== viewState . INDIVIDUALS ) {
// We switched views while in the process of fetching individuals -- any
// further work is useless.
return ;
}
dispatch ( { type : actions . FETCH _INDIVIDUALS _START } ) ;
try {
( { nodes } = yield heapWorker . getCensusIndividuals ( {
dominatorTreeId ,
indices ,
censusBreakdown ,
labelBreakdown : labelDisplay . breakdown ,
maxRetainingPaths : Preferences . get ( "devtools.memory.max-retaining-paths" ) ,
maxIndividuals : Preferences . get ( "devtools.memory.max-individuals" ) ,
} ) ) ;
} catch ( error ) {
reportException ( "actions/snapshot/fetchIndividuals" , error ) ;
dispatch ( { type : actions . INDIVIDUALS _ERROR , error } ) ;
return ;
}
}
while ( labelDisplay !== getState ( ) . labelDisplay ) ;
dispatch ( {
type : actions . FETCH _INDIVIDUALS _END ,
id ,
censusBreakdown ,
indices ,
labelDisplay ,
nodes ,
dominatorTree : snapshot _ . dominatorTree ,
} ) ;
} ;
} ;
/ * *
* Refresh the current individuals view .
*
* @ param { HeapAnalysesClient } heapWorker
* /
2016-05-17 21:25:54 +03:00
const refreshIndividuals = exports . refreshIndividuals = function ( heapWorker ) {
return function * ( dispatch , getState ) {
2016-04-12 04:04:31 +03:00
assert ( getState ( ) . view . state === viewState . INDIVIDUALS ,
"Should be in INDIVIDUALS view." ) ;
const { individuals } = getState ( ) ;
switch ( individuals . state ) {
case individualsState . COMPUTING _DOMINATOR _TREE :
case individualsState . FETCHING :
// Nothing to do here.
return ;
case individualsState . FETCHED :
if ( getState ( ) . individuals . labelDisplay === getState ( ) . labelDisplay ) {
return ;
}
break ;
case individualsState . ERROR :
// Doesn't hurt to retry: maybe we won't get an error this time around?
break ;
default :
assert ( false , ` Unexpected individuals state: ${ individuals . state } ` ) ;
return ;
}
yield dispatch ( fetchIndividuals ( heapWorker ,
individuals . id ,
individuals . censusBreakdown ,
individuals . indices ) ) ;
} ;
} ;
2015-10-27 19:11:05 +03:00
/ * *
* Refresh the selected snapshot ' s census data , if need be ( for example ,
2016-03-02 03:20:45 +03:00
* display configuration changed ) .
2015-10-27 19:11:05 +03:00
*
* @ param { HeapAnalysesClient } heapWorker
* /
const refreshSelectedCensus = exports . refreshSelectedCensus = function ( heapWorker ) {
2016-05-17 21:25:54 +03:00
return function * ( dispatch , getState ) {
2015-10-27 19:11:05 +03:00
let snapshot = getState ( ) . snapshots . find ( s => s . selected ) ;
2016-04-05 20:21:00 +03:00
if ( ! snapshot || snapshot . state !== states . READ ) {
return ;
}
2015-10-27 19:11:05 +03:00
// Intermediate snapshot states will get handled by the task action that is
2016-04-01 17:56:00 +03:00
// orchestrating them. For example, if the snapshot census's state is
// SAVING, then the takeCensus action will keep taking a census until
2015-10-27 19:11:05 +03:00
// the inverted property matches the inverted state. If the snapshot is
// still in the process of being saved or read, the takeSnapshotAndCensus
// task action will follow through and ensure that a census is taken.
2016-04-05 20:21:00 +03:00
if ( ( snapshot . census && snapshot . census . state === censusState . SAVED ) ||
2016-04-01 17:56:00 +03:00
! snapshot . census ) {
2015-11-13 22:20:45 +03:00
yield dispatch ( takeCensus ( heapWorker , snapshot . id ) ) ;
2015-10-27 19:11:05 +03:00
}
2015-09-26 06:09:58 +03:00
} ;
} ;
2015-10-10 18:09:28 +03:00
2016-04-01 17:56:00 +03:00
/ * *
* Refresh the selected snapshot ' s tree map data , if need be ( for example ,
* display configuration changed ) .
*
* @ param { HeapAnalysesClient } heapWorker
* /
const refreshSelectedTreeMap = exports . refreshSelectedTreeMap = function ( heapWorker ) {
2016-05-17 21:25:54 +03:00
return function * ( dispatch , getState ) {
2016-04-01 17:56:00 +03:00
let snapshot = getState ( ) . snapshots . find ( s => s . selected ) ;
2016-04-15 10:36:00 +03:00
if ( ! snapshot || snapshot . state !== states . READ ) {
return ;
}
2016-04-01 17:56:00 +03:00
// Intermediate snapshot states will get handled by the task action that is
// orchestrating them. For example, if the snapshot census's state is
// SAVING, then the takeCensus action will keep taking a census until
// the inverted property matches the inverted state. If the snapshot is
// still in the process of being saved or read, the takeSnapshotAndCensus
// task action will follow through and ensure that a census is taken.
2016-04-15 10:36:00 +03:00
if ( ( snapshot . treeMap && snapshot . treeMap . state === treeMapState . SAVED ) ||
2016-04-01 17:56:00 +03:00
! snapshot . treeMap ) {
yield dispatch ( takeTreeMap ( heapWorker , snapshot . id ) ) ;
}
} ;
} ;
2016-01-13 23:27:30 +03:00
/ * *
* Request that the ` HeapAnalysesWorker ` compute the dominator tree for the
* snapshot with the given ` id ` .
*
* @ param { HeapAnalysesClient } heapWorker
* @ param { SnapshotId } id
*
* @ returns { Promise < DominatorTreeId > }
* /
2016-04-12 04:04:31 +03:00
const computeDominatorTree = exports . computeDominatorTree =
TaskCache . declareCacheableTask ( {
getCacheKey ( _ , id ) {
return id ;
} ,
2016-05-17 21:25:54 +03:00
task : function * ( heapWorker , id , removeFromCache , dispatch , getState ) {
2016-01-13 23:27:30 +03:00
const snapshot = getSnapshot ( getState ( ) , id ) ;
assert ( ! ( snapshot . dominatorTree && snapshot . dominatorTree . dominatorTreeId ) ,
"Should not re-compute dominator trees" ) ;
dispatch ( { type : actions . COMPUTE _DOMINATOR _TREE _START , id } ) ;
let dominatorTreeId ;
try {
dominatorTreeId = yield heapWorker . computeDominatorTree ( snapshot . path ) ;
} catch ( error ) {
2016-04-12 04:04:31 +03:00
removeFromCache ( ) ;
2016-01-13 23:27:30 +03:00
reportException ( "actions/snapshot/computeDominatorTree" , error ) ;
dispatch ( { type : actions . DOMINATOR _TREE _ERROR , id , error } ) ;
return null ;
}
2016-04-12 04:04:31 +03:00
removeFromCache ( ) ;
2016-01-13 23:27:30 +03:00
dispatch ( { type : actions . COMPUTE _DOMINATOR _TREE _END , id , dominatorTreeId } ) ;
return dominatorTreeId ;
2016-04-12 04:04:31 +03:00
}
} ) ;
2016-01-13 23:27:30 +03:00
/ * *
* Get the partial subtree , starting from the root , of the
* snapshot - with - the - given - id ' s dominator tree .
*
* @ param { HeapAnalysesClient } heapWorker
* @ param { SnapshotId } id
*
* @ returns { Promise < DominatorTreeNode > }
* /
2016-04-12 04:04:31 +03:00
const fetchDominatorTree = exports . fetchDominatorTree =
TaskCache . declareCacheableTask ( {
getCacheKey ( _ , id ) {
return id ;
} ,
2016-05-17 21:25:54 +03:00
task : function * ( heapWorker , id , removeFromCache , dispatch , getState ) {
2016-01-13 23:27:30 +03:00
const snapshot = getSnapshot ( getState ( ) , id ) ;
assert ( dominatorTreeIsComputed ( snapshot ) ,
"Should have dominator tree model and it should be computed" ) ;
2016-03-02 03:20:45 +03:00
let display ;
2016-01-13 23:27:30 +03:00
let root ;
do {
2016-04-12 04:04:31 +03:00
display = getState ( ) . labelDisplay ;
2016-03-02 03:20:45 +03:00
assert ( display && display . breakdown ,
` Should have a breakdown to describe nodes with, got: ${ uneval ( display ) } ` ) ;
2016-01-13 23:27:30 +03:00
2016-03-02 03:20:45 +03:00
dispatch ( { type : actions . FETCH _DOMINATOR _TREE _START , id , display } ) ;
2016-01-13 23:27:30 +03:00
try {
root = yield heapWorker . getDominatorTree ( {
dominatorTreeId : snapshot . dominatorTree . dominatorTreeId ,
2016-03-02 03:20:45 +03:00
breakdown : display . breakdown ,
2016-04-12 04:04:31 +03:00
maxRetainingPaths : Preferences . get ( "devtools.memory.max-retaining-paths" ) ,
2016-01-13 23:27:30 +03:00
} ) ;
} catch ( error ) {
2016-04-12 04:04:31 +03:00
removeFromCache ( ) ;
2016-01-13 23:27:30 +03:00
reportException ( "actions/snapshot/fetchDominatorTree" , error ) ;
dispatch ( { type : actions . DOMINATOR _TREE _ERROR , id , error } ) ;
return null ;
}
}
2016-04-12 04:04:31 +03:00
while ( display !== getState ( ) . labelDisplay ) ;
2016-01-13 23:27:30 +03:00
2016-04-12 04:04:31 +03:00
removeFromCache ( ) ;
2016-01-13 23:27:30 +03:00
dispatch ( { type : actions . FETCH _DOMINATOR _TREE _END , id , root } ) ;
2016-03-02 03:20:45 +03:00
telemetry . countDominatorTree ( { display } ) ;
2016-01-13 23:27:30 +03:00
return root ;
2016-04-12 04:04:31 +03:00
}
} ) ;
2016-01-13 23:27:30 +03:00
/ * *
* Fetch the immediately dominated children represented by the placeholder
* ` lazyChildren ` from snapshot - with - the - given - id ' s dominator tree .
*
* @ param { HeapAnalysesClient } heapWorker
* @ param { SnapshotId } id
* @ param { DominatorTreeLazyChildren } lazyChildren
* /
2016-04-12 04:04:31 +03:00
const fetchImmediatelyDominated = exports . fetchImmediatelyDominated =
TaskCache . declareCacheableTask ( {
getCacheKey ( _ , id , lazyChildren ) {
return ` ${ id } - ${ lazyChildren . key ( ) } ` ;
} ,
2016-05-17 21:25:54 +03:00
task : function * ( heapWorker , id , lazyChildren , removeFromCache , dispatch , getState ) {
2016-01-13 23:27:30 +03:00
const snapshot = getSnapshot ( getState ( ) , id ) ;
assert ( snapshot . dominatorTree , "Should have dominator tree model" ) ;
assert ( snapshot . dominatorTree . state === dominatorTreeState . LOADED ||
snapshot . dominatorTree . state === dominatorTreeState . INCREMENTAL _FETCHING ,
"Cannot fetch immediately dominated nodes in a dominator tree unless " +
" the dominator tree has already been computed" ) ;
2016-03-02 03:20:45 +03:00
let display ;
2016-01-13 23:27:30 +03:00
let response ;
do {
2016-04-12 04:04:31 +03:00
display = getState ( ) . labelDisplay ;
2016-03-02 03:20:45 +03:00
assert ( display , "Should have a display to describe nodes with." ) ;
2016-01-13 23:27:30 +03:00
dispatch ( { type : actions . FETCH _IMMEDIATELY _DOMINATED _START , id } ) ;
try {
response = yield heapWorker . getImmediatelyDominated ( {
dominatorTreeId : snapshot . dominatorTree . dominatorTreeId ,
2016-03-02 03:20:45 +03:00
breakdown : display . breakdown ,
2016-01-13 23:27:30 +03:00
nodeId : lazyChildren . parentNodeId ( ) ,
startIndex : lazyChildren . siblingIndex ( ) ,
2016-04-12 04:04:31 +03:00
maxRetainingPaths : Preferences . get ( "devtools.memory.max-retaining-paths" ) ,
2016-01-13 23:27:30 +03:00
} ) ;
} catch ( error ) {
2016-04-12 04:04:31 +03:00
removeFromCache ( ) ;
2016-01-13 23:27:30 +03:00
reportException ( "actions/snapshot/fetchImmediatelyDominated" , error ) ;
dispatch ( { type : actions . DOMINATOR _TREE _ERROR , id , error } ) ;
return null ;
}
}
2016-04-12 04:04:31 +03:00
while ( display !== getState ( ) . labelDisplay ) ;
2016-01-13 23:27:30 +03:00
2016-04-12 04:04:31 +03:00
removeFromCache ( ) ;
2016-01-13 23:27:30 +03:00
dispatch ( {
type : actions . FETCH _IMMEDIATELY _DOMINATED _END ,
id ,
path : response . path ,
nodes : response . nodes ,
moreChildrenAvailable : response . moreChildrenAvailable ,
} ) ;
2016-04-12 04:04:31 +03:00
}
} ) ;
2016-01-13 23:27:30 +03:00
/ * *
* Compute and then fetch the dominator tree of the snapshot with the given
* ` id ` .
*
* @ param { HeapAnalysesClient } heapWorker
* @ param { SnapshotId } id
*
* @ returns { Promise < DominatorTreeNode > }
* /
2016-04-12 04:04:31 +03:00
const computeAndFetchDominatorTree = exports . computeAndFetchDominatorTree =
TaskCache . declareCacheableTask ( {
getCacheKey ( _ , id ) {
return id ;
} ,
2016-05-17 21:25:54 +03:00
task : function * ( heapWorker , id , removeFromCache , dispatch , getState ) {
2016-01-13 23:27:30 +03:00
const dominatorTreeId = yield dispatch ( computeDominatorTree ( heapWorker , id ) ) ;
if ( dominatorTreeId === null ) {
2016-04-12 04:04:31 +03:00
removeFromCache ( ) ;
2016-01-13 23:27:30 +03:00
return null ;
}
const root = yield dispatch ( fetchDominatorTree ( heapWorker , id ) ) ;
2016-04-12 04:04:31 +03:00
removeFromCache ( ) ;
2016-01-13 23:27:30 +03:00
if ( ! root ) {
return null ;
}
return root ;
2016-04-12 04:04:31 +03:00
}
} ) ;
2016-01-13 23:27:30 +03:00
/ * *
* Update the currently selected snapshot ' s dominator tree .
*
* @ param { HeapAnalysesClient } heapWorker
* /
const refreshSelectedDominatorTree = exports . refreshSelectedDominatorTree = function ( heapWorker ) {
2016-05-17 21:25:54 +03:00
return function * ( dispatch , getState ) {
2016-01-13 23:27:30 +03:00
let snapshot = getState ( ) . snapshots . find ( s => s . selected ) ;
if ( ! snapshot ) {
return ;
}
if ( snapshot . dominatorTree &&
! ( snapshot . dominatorTree . state === dominatorTreeState . COMPUTED ||
snapshot . dominatorTree . state === dominatorTreeState . LOADED ||
snapshot . dominatorTree . state === dominatorTreeState . INCREMENTAL _FETCHING ) ) {
return ;
}
2016-04-01 17:56:00 +03:00
if ( snapshot . state === states . READ ) {
if ( snapshot . dominatorTree ) {
yield dispatch ( fetchDominatorTree ( heapWorker , snapshot . id ) ) ;
} else {
yield dispatch ( computeAndFetchDominatorTree ( heapWorker , snapshot . id ) ) ;
}
} else {
2016-01-13 23:27:30 +03:00
// If there was an error, we can't continue. If we are still saving or
// reading the snapshot, then takeSnapshotAndCensus will finish the job
// for us.
2016-05-17 21:25:54 +03:00
return ;
2016-01-13 23:27:30 +03:00
}
} ;
} ;
2015-10-10 18:09:28 +03:00
/ * *
2015-11-13 22:20:45 +03:00
* Select the snapshot with the given id .
*
* @ param { snapshotId } id
2015-10-17 05:15:54 +03:00
* @ see { Snapshot } model defined in devtools / client / memory / models . js
2015-10-10 18:09:28 +03:00
* /
2015-11-13 22:20:45 +03:00
const selectSnapshot = exports . selectSnapshot = function ( id ) {
2015-10-10 18:09:28 +03:00
return {
type : actions . SELECT _SNAPSHOT ,
2015-11-13 22:20:45 +03:00
id
2015-10-10 18:09:28 +03:00
} ;
} ;
2016-01-13 23:27:30 +03:00
2016-01-12 21:14:15 +03:00
/ * *
2016-04-01 17:56:00 +03:00
* Delete all snapshots that are in the READ or ERROR state
2016-01-12 21:14:15 +03:00
*
* @ param { HeapAnalysesClient } heapWorker
* /
const clearSnapshots = exports . clearSnapshots = function ( heapWorker ) {
2016-05-17 21:25:54 +03:00
return function * ( dispatch , getState ) {
2016-04-01 17:56:00 +03:00
let snapshots = getState ( ) . snapshots . filter ( s => {
let snapshotReady = s . state === states . READ || s . state === states . ERROR ;
let censusReady = ( s . treeMap && s . treeMap . state === treeMapState . SAVED ) ||
( s . census && s . census . state === censusState . SAVED ) ;
2016-04-12 04:04:31 +03:00
return snapshotReady && censusReady ;
2016-04-01 17:56:00 +03:00
} ) ;
2016-01-12 21:14:15 +03:00
let ids = snapshots . map ( s => s . id ) ;
dispatch ( { type : actions . DELETE _SNAPSHOTS _START , ids } ) ;
2016-03-07 23:23:32 +03:00
if ( getState ( ) . diffing ) {
dispatch ( diffing . toggleDiffing ( ) ) ;
}
2016-04-12 04:04:31 +03:00
if ( getState ( ) . individuals ) {
dispatch ( view . popView ( ) ) ;
}
2016-03-07 23:23:32 +03:00
2016-02-02 17:09:00 +03:00
yield Promise . all ( snapshots . map ( snapshot => {
return heapWorker . deleteHeapSnapshot ( snapshot . path ) . catch ( error => {
2016-01-12 21:14:15 +03:00
reportException ( "clearSnapshots" , error ) ;
2016-02-02 17:09:00 +03:00
dispatch ( { type : actions . SNAPSHOT _ERROR , id : snapshot . id , error } ) ;
2016-01-12 21:14:15 +03:00
} ) ;
} ) ) ;
dispatch ( { type : actions . DELETE _SNAPSHOTS _END , ids } ) ;
} ;
} ;
2016-04-15 10:36:00 +03:00
/ * *
* Delete a snapshot
*
* @ param { HeapAnalysesClient } heapWorker
* @ param { snapshotModel } snapshot
* /
const deleteSnapshot = exports . deleteSnapshot = function ( heapWorker , snapshot ) {
2016-05-17 21:25:54 +03:00
return function * ( dispatch , getState ) {
2016-04-15 10:36:00 +03:00
dispatch ( { type : actions . DELETE _SNAPSHOTS _START , ids : [ snapshot . id ] } ) ;
try {
yield heapWorker . deleteHeapSnapshot ( snapshot . path ) ;
} catch ( error ) {
reportException ( "deleteSnapshot" , error ) ;
dispatch ( { type : actions . SNAPSHOT _ERROR , id : snapshot . id , error } ) ;
}
dispatch ( { type : actions . DELETE _SNAPSHOTS _END , ids : [ snapshot . id ] } ) ;
} ;
} ;
2016-01-13 23:27:30 +03:00
/ * *
* Expand the given node in the snapshot ' s census report .
*
* @ param { CensusTreeNode } node
* /
const expandCensusNode = exports . expandCensusNode = function ( id , node ) {
return {
type : actions . EXPAND _CENSUS _NODE ,
id ,
node ,
} ;
} ;
/ * *
* Collapse the given node in the snapshot ' s census report .
*
* @ param { CensusTreeNode } node
* /
const collapseCensusNode = exports . collapseCensusNode = function ( id , node ) {
return {
type : actions . COLLAPSE _CENSUS _NODE ,
id ,
node ,
} ;
} ;
/ * *
* Focus the given node in the snapshot 's census' s report .
*
* @ param { SnapshotId } id
* @ param { DominatorTreeNode } node
* /
const focusCensusNode = exports . focusCensusNode = function ( id , node ) {
return {
type : actions . FOCUS _CENSUS _NODE ,
id ,
node ,
} ;
} ;
/ * *
* Expand the given node in the snapshot ' s dominator tree .
*
* @ param { DominatorTreeTreeNode } node
* /
const expandDominatorTreeNode = exports . expandDominatorTreeNode = function ( id , node ) {
return {
type : actions . EXPAND _DOMINATOR _TREE _NODE ,
id ,
node ,
} ;
} ;
/ * *
* Collapse the given node in the snapshot ' s dominator tree .
*
* @ param { DominatorTreeTreeNode } node
* /
const collapseDominatorTreeNode = exports . collapseDominatorTreeNode = function ( id , node ) {
return {
type : actions . COLLAPSE _DOMINATOR _TREE _NODE ,
id ,
node ,
} ;
} ;
/ * *
* Focus the given node in the snapshot ' s dominator tree .
*
* @ param { SnapshotId } id
* @ param { DominatorTreeNode } node
* /
const focusDominatorTreeNode = exports . focusDominatorTreeNode = function ( id , node ) {
return {
type : actions . FOCUS _DOMINATOR _TREE _NODE ,
id ,
node ,
} ;
} ;