Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  snapshot.js   Sprache: JAVA

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * 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";

const {
  assert,
  reportException,
  isSet,
} = require("resource://devtools/shared/DevToolsUtils.js");
const {
  censusIsUpToDate,
  getSnapshot,
  createSnapshot,
  dominatorTreeIsComputed,
} = require("resource://devtools/client/memory/utils.js");
const {
  actions,
  snapshotState: states,
  viewState,
  censusState,
  treeMapState,
  dominatorTreeState,
  individualsState,
} = require("resource://devtools/client/memory/constants.js");
const view = require("resource://devtools/client/memory/actions/view.js");
const refresh = require("resource://devtools/client/memory/actions/refresh.js");
const diffing = require("resource://devtools/client/memory/actions/diffing.js");
const TaskCache = require("resource://devtools/client/memory/actions/task-cache.js");

/**
 * A series of actions are fired from this task to save, read and generate the
 * initial census from a snapshot.
 *
 * @param {MemoryFront}
 * @param {HeapAnalysesClient}
 * @param {Object}
 */

exports.takeSnapshotAndCensus = function (front, heapWorker) {
  return async function ({ dispatch, getState }) {
    const id = await dispatch(takeSnapshot(front));
    if (id === null) {
      return;
    }

    await dispatch(readSnapshot(heapWorker, id));
    if (getSnapshot(getState(), id).state !== states.READ) {
      return;
    }

    await 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
 */

const computeSnapshotData = (exports.computeSnapshotData = function (
  heapWorker,
  id
) {
  return async function ({ dispatch, getState }) {
    if (getSnapshot(getState(), id).state !== states.READ) {
      return;
    }

    // Decide which type of census to take.
    const censusTaker = getCurrentCensusTaker(getState().view.state);
    await dispatch(censusTaker(heapWorker, id));

    if (
      getState().view.state === viewState.DOMINATOR_TREE &&
      !getSnapshot(getState(), id).dominatorTree
    ) {
      await dispatch(computeAndFetchDominatorTree(heapWorker, id));
    }
  };
});

/**
 * Selects a snapshot and if the snapshot's census is using a different
 * display, take a new census.
 *
 * @param {HeapAnalysesClient} heapWorker
 * @param {snapshotId} id
 */

exports.selectSnapshotAndRefresh = function (heapWorker, id) {
  return async function ({ dispatch, getState }) {
    if (getState().diffing || getState().individuals) {
      dispatch(view.changeView(viewState.CENSUS));
    }

    dispatch(selectSnapshot(id));
    await dispatch(refresh.refresh(heapWorker));
  };
};

/**
 * Take a snapshot and return its id on success, or null on failure.
 *
 * @param {MemoryFront} front
 * @returns {Number|null}
 */

const takeSnapshot = (exports.takeSnapshot = function (front) {
  return async function ({ dispatch, getState }) {
    if (getState().diffing || getState().individuals) {
      dispatch(view.changeView(viewState.CENSUS));
    }

    const snapshot = createSnapshot(getState());
    const id = snapshot.id;
    dispatch({ type: actions.TAKE_SNAPSHOT_START, snapshot });
    dispatch(selectSnapshot(id));

    let path;
    try {
      path = await front.saveHeapSnapshot();
    } catch (error) {
      reportException("takeSnapshot", error);
      dispatch({ type: actions.SNAPSHOT_ERROR, id, error });
      return null;
    }

    dispatch({ type: actions.TAKE_SNAPSHOT_END, id, path });
    return snapshot.id;
  };
});

/**
 * Reads a snapshot into memory; necessary to do before taking
 * a census on the snapshot. May only be called once per snapshot.
 *
 * @param {HeapAnalysesClient} heapWorker
 * @param {snapshotId} id
 */

const readSnapshot = (exports.readSnapshot = TaskCache.declareCacheableTask({
  getCacheKey(_, id) {
    return id;
  },

  async task(heapWorker, id, removeFromCache, dispatch, getState) {
    const snapshot = getSnapshot(getState(), id);
    assert(
      [states.SAVED, states.IMPORTING].includes(snapshot.state),
      `Should only read a snapshot once. Found snapshot in state ${snapshot.state}`
    );

    let creationTime;

    dispatch({ type: actions.READ_SNAPSHOT_START, id });
    try {
      await heapWorker.readHeapSnapshot(snapshot.path);
      creationTime = await heapWorker.getCreationTime(snapshot.path);
    } catch (error) {
      removeFromCache();
      reportException("readSnapshot", error);
      dispatch({ type: actions.SNAPSHOT_ERROR, id, error });
      return;
    }

    removeFromCache();
    dispatch({ type: actions.READ_SNAPSHOT_END, id, creationTime });
  },
}));

let takeCensusTaskCounter = 0;

/**
 * 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.
 *
 * @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.
 */

function makeTakeCensusTask({
  getDisplay,
  getFilter,
  getCensus,
  beginAction,
  endAction,
  errorAction,
  canTakeCensus,
}) {
  /**
   * @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
   */

  const thisTakeCensusTaskId = ++takeCensusTaskCounter;
  return TaskCache.declareCacheableTask({
    getCacheKey(_, id) {
      return `take-census-task-${thisTakeCensusTaskId}-${id}`;
    },

    async task(heapWorker, id, removeFromCache, dispatch, getState) {
      const snapshot = getSnapshot(getState(), id);
      if (!snapshot) {
        removeFromCache();
        return;
      }

      // Assert that snapshot is in a valid state
      assert(
        canTakeCensus(snapshot),
        "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}`
      );

      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))) {
        removeFromCache();
        return;
      }

      // 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,
        });

        const opts = display.inverted
          ? { asInvertedTreeNode: true }
          : { asTreeNode: true };

        opts.filter = filter || null;

        try {
          ({ report, parentMap } = await heapWorker.takeCensus(
            snapshot.path,
            { breakdown: display.breakdown },
            opts
          ));
        } catch (error) {
          removeFromCache();
          reportException("takeCensus", error);
          dispatch({ type: errorAction, id, error });
          return;
        }
      } while (
        filter !== getState().filter ||
        display !== getDisplay(getState())
      );

      removeFromCache();
      dispatch({
        type: endAction,
        id,
        display,
        filter,
        report,
        parentMap,
      });
    },
  });
}

/**
 * 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,
  errorAction: actions.TAKE_CENSUS_ERROR,
  canTakeCensus: snapshot =>
    snapshot.state === states.READ &&
    (!snapshot.census || snapshot.census.state === censusState.SAVED),
}));

/**
 * 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,
  errorAction: actions.TAKE_TREE_MAP_ERROR,
  canTakeCensus: snapshot =>
    snapshot.state === states.READ &&
    (!snapshot.treeMap || snapshot.treeMap.state === treeMapState.SAVED),
}));

/**
 * 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
 */

const getCurrentCensusTaker = (exports.getCurrentCensusTaker = function (
  currentView
) {
  switch (currentView) {
    case viewState.TREE_MAP:
      return takeTreeMap;
    case viewState.CENSUS:
      return takeCensus;
    default:
      return defaultCensusTaker;
  }
});

/**
 * Focus the given node in the individuals view.
 *
 * @param {DominatorTreeNode} node.
 */

exports.focusIndividual = function (node) {
  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 = function (
  heapWorker,
  id,
  censusBreakdown,
  reportLeafIndex
) {
  return async function ({ dispatch, getState }) {
    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)) {
      await dispatch(computeAndFetchDominatorTree(heapWorker, id));
    }

    const snapshot_ = getSnapshot(getState(), id);
    assert(
      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?.breakdown?.by,
        `Should have a breakdown to label nodes with, got: ${JSON.stringify(
          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 } = await heapWorker.getCensusIndividuals({
          dominatorTreeId,
          indices,
          censusBreakdown,
          labelBreakdown: labelDisplay.breakdown,
          maxRetainingPaths: Services.prefs.getIntPref(
            "devtools.memory.max-retaining-paths"
          ),
          maxIndividuals: Services.prefs.getIntPref(
            "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
 */

exports.refreshIndividuals = function (heapWorker) {
  return async function ({ dispatch, getState }) {
    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;
    }

    await dispatch(
      fetchIndividuals(
        heapWorker,
        individuals.id,
        individuals.censusBreakdown,
        individuals.indices
      )
    );
  };
};

/**
 * Refresh the selected snapshot's census data, if need be (for example,
 * display configuration changed).
 *
 * @param {HeapAnalysesClient} heapWorker
 */

exports.refreshSelectedCensus = function (heapWorker) {
  return async function ({ dispatch, getState }) {
    const snapshot = getState().snapshots.find(s => s.selected);
    if (!snapshot || snapshot.state !== states.READ) {
      return;
    }

    // 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.
    if (
      (snapshot.census && snapshot.census.state === censusState.SAVED) ||
      !snapshot.census
    ) {
      await dispatch(takeCensus(heapWorker, snapshot.id));
    }
  };
};

/**
 * Refresh the selected snapshot's tree map data, if need be (for example,
 * display configuration changed).
 *
 * @param {HeapAnalysesClient} heapWorker
 */

exports.refreshSelectedTreeMap = function (heapWorker) {
  return async function ({ dispatch, getState }) {
    const snapshot = getState().snapshots.find(s => s.selected);
    if (!snapshot || snapshot.state !== states.READ) {
      return;
    }

    // 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.
    if (
      (snapshot.treeMap && snapshot.treeMap.state === treeMapState.SAVED) ||
      !snapshot.treeMap
    ) {
      await dispatch(takeTreeMap(heapWorker, snapshot.id));
    }
  };
};

/**
 * Request that the `HeapAnalysesWorker` compute the dominator tree for the
 * snapshot with the given `id`.
 *
 * @param {HeapAnalysesClient} heapWorker
 * @param {SnapshotId} id
 *
 * @returns {Promise<DominatorTreeId>}
 */

const computeDominatorTree = (exports.computeDominatorTree =
  TaskCache.declareCacheableTask({
    getCacheKey(_, id) {
      return id;
    },

    async task(heapWorker, id, removeFromCache, dispatch, getState) {
      const snapshot = getSnapshot(getState(), id);
      assert(
        !snapshot.dominatorTree?.dominatorTreeId,
        "Should not re-compute dominator trees"
      );

      dispatch({ type: actions.COMPUTE_DOMINATOR_TREE_START, id });

      let dominatorTreeId;
      try {
        dominatorTreeId = await heapWorker.computeDominatorTree(snapshot.path);
      } catch (error) {
        removeFromCache();
        reportException("actions/snapshot/computeDominatorTree", error);
        dispatch({ type: actions.DOMINATOR_TREE_ERROR, id, error });
        return null;
      }

      removeFromCache();
      dispatch({
        type: actions.COMPUTE_DOMINATOR_TREE_END,
        id,
        dominatorTreeId,
      });
      return dominatorTreeId;
    },
  }));

/**
 * 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>}
 */

const fetchDominatorTree = (exports.fetchDominatorTree =
  TaskCache.declareCacheableTask({
    getCacheKey(_, id) {
      return id;
    },

    async task(heapWorker, id, removeFromCache, dispatch, getState) {
      const snapshot = getSnapshot(getState(), id);
      assert(
        dominatorTreeIsComputed(snapshot),
        "Should have dominator tree model and it should be computed"
      );

      let display;
      let root;
      do {
        display = getState().labelDisplay;
        assert(
          display?.breakdown,
          `Should have a breakdown to describe nodes with, got: ${JSON.stringify(
            display
          )}`
        );

        dispatch({ type: actions.FETCH_DOMINATOR_TREE_START, id, display });

        try {
          root = await heapWorker.getDominatorTree({
            dominatorTreeId: snapshot.dominatorTree.dominatorTreeId,
            breakdown: display.breakdown,
            maxRetainingPaths: Services.prefs.getIntPref(
              "devtools.memory.max-retaining-paths"
            ),
          });
        } catch (error) {
          removeFromCache();
          reportException("actions/snapshot/fetchDominatorTree", error);
          dispatch({ type: actions.DOMINATOR_TREE_ERROR, id, error });
          return null;
        }
      } while (display !== getState().labelDisplay);

      removeFromCache();
      dispatch({ type: actions.FETCH_DOMINATOR_TREE_END, id, root });
      return root;
    },
  }));

/**
 * 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
 */

exports.fetchImmediatelyDominated = TaskCache.declareCacheableTask({
  getCacheKey(_, id, lazyChildren) {
    return `${id}-${lazyChildren.key()}`;
  },

  async task(
    heapWorker,
    id,
    lazyChildren,
    removeFromCache,
    dispatch,
    getState
  ) {
    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"
    );

    let display;
    let response;
    do {
      display = getState().labelDisplay;
      assert(display, "Should have a display to describe nodes with.");

      dispatch({ type: actions.FETCH_IMMEDIATELY_DOMINATED_START, id });

      try {
        response = await heapWorker.getImmediatelyDominated({
          dominatorTreeId: snapshot.dominatorTree.dominatorTreeId,
          breakdown: display.breakdown,
          nodeId: lazyChildren.parentNodeId(),
          startIndex: lazyChildren.siblingIndex(),
          maxRetainingPaths: Services.prefs.getIntPref(
            "devtools.memory.max-retaining-paths"
          ),
        });
      } catch (error) {
        removeFromCache();
        reportException("actions/snapshot/fetchImmediatelyDominated", error);
        dispatch({ type: actions.DOMINATOR_TREE_ERROR, id, error });
        return;
      }
    } while (display !== getState().labelDisplay);

    removeFromCache();
    dispatch({
      type: actions.FETCH_IMMEDIATELY_DOMINATED_END,
      id,
      path: response.path,
      nodes: response.nodes,
      moreChildrenAvailable: response.moreChildrenAvailable,
    });
  },
});

/**
 * Compute and then fetch the dominator tree of the snapshot with the given
 * `id`.
 *
 * @param {HeapAnalysesClient} heapWorker
 * @param {SnapshotId} id
 *
 * @returns {Promise<DominatorTreeNode>}
 */

const computeAndFetchDominatorTree = (exports.computeAndFetchDominatorTree =
  TaskCache.declareCacheableTask({
    getCacheKey(_, id) {
      return id;
    },

    async task(heapWorker, id, removeFromCache, dispatch) {
      const dominatorTreeId = await dispatch(
        computeDominatorTree(heapWorker, id)
      );
      if (dominatorTreeId === null) {
        removeFromCache();
        return null;
      }

      const root = await dispatch(fetchDominatorTree(heapWorker, id));
      removeFromCache();

      if (!root) {
        return null;
      }

      return root;
    },
  }));

/**
 * Update the currently selected snapshot's dominator tree.
 *
 * @param {HeapAnalysesClient} heapWorker
 */

exports.refreshSelectedDominatorTree = function (heapWorker) {
  return async function ({ dispatch, getState }) {
    const 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;
    }

    // We need to check for the snapshot state because if there was an error,
    // we can't continue and if we are still saving or reading the snapshot,
    // then takeSnapshotAndCensus will finish the job for us
    if (snapshot.state === states.READ) {
      if (snapshot.dominatorTree) {
        await dispatch(fetchDominatorTree(heapWorker, snapshot.id));
      } else {
        await dispatch(computeAndFetchDominatorTree(heapWorker, snapshot.id));
      }
    }
  };
};

/**
 * Select the snapshot with the given id.
 *
 * @param {snapshotId} id
 * @see {Snapshot} model defined in devtools/client/memory/models.js
 */

const selectSnapshot = (exports.selectSnapshot = function (id) {
  return {
    type: actions.SELECT_SNAPSHOT,
    id,
  };
});

/**
 * Delete all snapshots that are in the READ or ERROR state
 *
 * @param {HeapAnalysesClient} heapWorker
 */

exports.clearSnapshots = function (heapWorker) {
  return async function ({ dispatch, getState }) {
    const snapshots = getState().snapshots.filter(s => {
      const snapshotReady = s.state === states.READ || s.state === states.ERROR;
      const censusReady =
        (s.treeMap && s.treeMap.state === treeMapState.SAVED) ||
        (s.census && s.census.state === censusState.SAVED);

      return snapshotReady && censusReady;
    });

    const ids = snapshots.map(s => s.id);

    dispatch({ type: actions.DELETE_SNAPSHOTS_START, ids });

    if (getState().diffing) {
      dispatch(diffing.toggleDiffing());
    }
    if (getState().individuals) {
      dispatch(view.popView());
    }

    await Promise.all(
      snapshots.map(snapshot => {
        return heapWorker.deleteHeapSnapshot(snapshot.path).catch(error => {
          reportException("clearSnapshots", error);
          dispatch({ type: actions.SNAPSHOT_ERROR, id: snapshot.id, error });
        });
      })
    );

    dispatch({ type: actions.DELETE_SNAPSHOTS_END, ids });
  };
};

/**
 * Delete a snapshot
 *
 * @param {HeapAnalysesClient} heapWorker
 * @param {snapshotModel} snapshot
 */

exports.deleteSnapshot = function (heapWorker, snapshot) {
  return async function ({ dispatch }) {
    dispatch({ type: actions.DELETE_SNAPSHOTS_START, ids: [snapshot.id] });

    try {
      await 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] });
  };
};

/**
 * Expand the given node in the snapshot's census report.
 *
 * @param {CensusTreeNode} node
 */

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
 */

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
 */

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
 */

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
 */

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
 */

exports.focusDominatorTreeNode = function (id, node) {
  return {
    type: actions.FOCUS_DOMINATOR_TREE_NODE,
    id,
    node,
  };
};

Messung V0.5
C=94 H=99 G=96

¤ Dauer der Verarbeitung: 0.37 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge