Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

Commit

Permalink
Add contract names to stacktraces
Browse files Browse the repository at this point in the history
  • Loading branch information
haltman-at committed Apr 17, 2020
1 parent 8d120a5 commit 19c7ea2
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 34 deletions.
15 changes: 11 additions & 4 deletions packages/debug-utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -560,8 +560,15 @@ var DebugUtils = {
//we want to print inner to outer, so first, let's
//reverse
stacktrace = stacktrace.slice().reverse(); //reverse is in-place so clone first
let lines = stacktrace.map(({ name, location }) => {
let functionName = name ? `function ${name}` : "unknown function";
let lines = stacktrace.map(({ functionName, contractName, location }) => {
let name;
if (contractName && functionName) {
name = `${contractName}.${functionName}`;
} else if (contractName) {
name = contractName;
} else {
name = "unknown function";
}
if (location) {
let {
source: { sourcePath },
Expand All @@ -571,9 +578,9 @@ var DebugUtils = {
}
}
} = location;
return `at ${functionName} (${sourcePath}:${line + 1}:${column + 1})`; //add 1 to account for 0-indexing
return `at ${name} (${sourcePath}:${line + 1}:${column + 1})`; //add 1 to account for 0-indexing
} else {
return `at ${functionName} (unknown location)`;
return `at ${name} (unknown location)`;
}
});
let status = stacktrace[0].status;
Expand Down
8 changes: 5 additions & 3 deletions packages/debugger/lib/stacktrace/actions/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
export const JUMP_IN = "STACKTRACE_JUMP_IN";
export function jumpIn(location, functionNode) {
export function jumpIn(location, functionNode, contractNode) {
return {
type: JUMP_IN,
location,
functionNode
functionNode,
contractNode
};
}

Expand All @@ -16,10 +17,11 @@ export function jumpOut(location) {
}

export const EXTERNAL_CALL = "STACKTRACE_EXTERNAL_CALL";
export function externalCall(location, skippedInReports) {
export function externalCall(location, contractNode, skippedInReports) {
return {
type: EXTERNAL_CALL,
location,
contractNode,
skippedInReports
};
}
Expand Down
15 changes: 12 additions & 3 deletions packages/debugger/lib/stacktrace/reducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,18 @@ function callstack(state = [], action) {
let newFrame;
switch (action.type) {
case actions.JUMP_IN:
let { location, functionNode } = action;
let { location, functionNode, contractNode } = action;
newFrame = {
type: "internal",
calledFromLocation: location,
name:
functionName:
functionNode && functionNode.nodeType === "FunctionDefinition"
? functionNode.name
: undefined,
contractName:
contractNode && contractNode.nodeType === "ContractDefinition"
? contractNode.name
: undefined,
//note we don't currently account for getters because currently
//we can't; fallback, receive, constructors, & modifiers also remain
//unaccounted for at present
Expand All @@ -37,7 +41,12 @@ function callstack(state = [], action) {
type: "external",
calledFromLocation: action.location,
skippedInReports: action.skippedInReports,
name: undefined
functionName: undefined,
contractName:
action.contractNode &&
action.contractNode.nodeType === "ContractDefinition"
? action.contractNode.name
: undefined
};
return [...state, newFrame];
case actions.EXECUTE_RETURN:
Expand Down
13 changes: 9 additions & 4 deletions packages/debugger/lib/stacktrace/sagas/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ function* stacktraceSaga() {
//(an EXTERNAL_RETURN, OTOH, is obviously exclusive of the possibilities below)
if (yield select(stacktrace.current.willJumpIn)) {
const nextLocation = yield select(stacktrace.next.location);
yield put(actions.jumpIn(currentLocation, nextLocation.node)); //in this case we only want the node
const nextParent = yield select(stacktrace.next.contractNode);
yield put(actions.jumpIn(currentLocation, nextLocation.node, nextParent));
positionUpdated = true;
} else if (yield select(stacktrace.current.willJumpOut)) {
yield put(actions.jumpOut(currentLocation));
Expand All @@ -64,7 +65,10 @@ function* stacktraceSaga() {
const skipInReports = yield select(
stacktrace.current.nextFrameIsSkippedInReports
);
yield put(actions.externalCall(currentLocation, skipInReports));
const nextLocation = yield select(stacktrace.next.location);
yield put(
actions.externalCall(currentLocation, nextLocation.node, skipInReports)
);
positionUpdated = true;
}
//finally, if no other action updated the position, do so here
Expand All @@ -83,9 +87,10 @@ export function* unload() {

export function* begin() {
const skipInReports = yield select(
stacktrace.current.nextFrameIsSkippedInReports
stacktrace.transaction.bottomFrameIsSkippedInReports
);
yield put(actions.externalCall(null, skipInReports));
const currentLocation = yield select(stacktrace.current.location);
yield put(actions.externalCall(null, currentLocation.node, skipInReports));
}

export function* saga() {
Expand Down
54 changes: 46 additions & 8 deletions packages/debugger/lib/stacktrace/selectors/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { createSelectorTree, createLeaf } from "reselect-tree";

import solidity from "lib/solidity/selectors";

import jsonpointer from "json-pointer";
import zipWith from "lodash.zipwith";
import { popNWhere } from "lib/helpers";

Expand All @@ -23,9 +24,8 @@ function generateReport(rawStack, location, status) {
rawStack[i + 1].type === "internal"
) {
const combinedFrame = {
//only including the relevant info here
calledFromLocation: frame.calledFromLocation,
name: rawStack[i + 1].name
...rawStack[i + 1],
calledFromLocation: frame.calledFromLocation
};
callstack.push(combinedFrame);
i++; //!! SKIP THE NEXT FRAME!
Expand All @@ -41,11 +41,14 @@ function generateReport(rawStack, location, status) {
locations.shift();
locations.push(location);
debug("locations: %O", locations);
const names = callstack.map(frame => frame.name);
const names = callstack.map(({ functionName, contractName }) => ({
functionName,
contractName
}));
debug("names: %O", names);
let report = zipWith(locations, names, (location, name) => ({
location,
name
let report = zipWith(locations, names, (location, nameInfo) => ({
...nameInfo,
location
}));
//finally: set the status in the top frame
if (status !== null) {
Expand All @@ -71,7 +74,11 @@ function createMultistepSelectors(stepSelector) {
/**
* .node
*/
node: createLeaf([stepSelector.node], identity)
node: createLeaf([stepSelector.node], identity),
/**
* .pointer
*/
pointer: createLeaf([stepSelector.pointer], identity)
},

/**
Expand All @@ -83,6 +90,24 @@ function createMultistepSelectors(stepSelector) {
source: { id, compilationId, sourcePath },
sourceRange
})
),

/**
* .contractNode
* WARNING: ad-hoc selector only meant to be used
* when you're on a function node!
* should probably be replaced by something better;
* the data submodule handles these things a better way
*/
contractNode: createLeaf(
["./location/source", "./location/pointer"],
({ ast }, pointer) =>
pointer
? jsonpointer.get(
ast,
pointer.replace(/\/nodes\/\d+$/, "") //cut off end
)
: ast
)
};
}
Expand All @@ -93,6 +118,19 @@ let stacktrace = createSelectorTree({
*/
state: state => state.stacktrace,

/**
* stacktrace.transaction
*/
transaction: {
/**
* stacktrace.transaction.bottomFrameIsSkippedInReports
*/
bottomFrameIsSkippedInReports: createLeaf(
[solidity.transaction.bottomStackframeRequiresPhantomFrame],
identity
)
},

/**
* stacktrace.current
*/
Expand Down
59 changes: 47 additions & 12 deletions packages/debugger/test/stacktrace.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,16 @@ describe("Stack tracing", function() {
await session.continueUntilBreakpoint(); //run till end

let report = session.view(stacktrace.current.finalReport);
let names = report.map(({ name }) => name);
assert.deepEqual(names, ["run", "run3", "run2", "run1", "runRequire"]);
let functionNames = report.map(({ functionName }) => functionName);
assert.deepEqual(functionNames, [
"run",
"run3",
"run2",
"run1",
"runRequire"
]);
let contractNames = report.map(({ contractName }) => contractName);
assert(contractNames.every(name => name === "StacktraceTest"));
let status = report[report.length - 1].status;
assert.isFalse(status);
let location = report[report.length - 1].location;
Expand Down Expand Up @@ -179,8 +187,10 @@ describe("Stack tracing", function() {
await session.continueUntilBreakpoint(); //run till EMIT

let report = session.view(stacktrace.current.report);
let names = report.map(({ name }) => name);
assert.deepEqual(names, ["run", "run2", "run1", "runRequire"]);
let functionNames = report.map(({ functionName }) => functionName);
assert.deepEqual(functionNames, ["run", "run2", "run1", "runRequire"]);
let contractNames = report.map(({ contractName }) => contractName);
assert(contractNames.every(name => name === "StacktraceTest"));
let status = report[report.length - 1].status;
assert.isUndefined(status);
let location = report[report.length - 1].location;
Expand All @@ -191,8 +201,16 @@ describe("Stack tracing", function() {
await session.continueUntilBreakpoint(); //run till EMIT again

report = session.view(stacktrace.current.report);
names = report.map(({ name }) => name);
assert.deepEqual(names, ["run", "run3", "run2", "run1", "runRequire"]);
functionNames = report.map(({ functionName }) => functionName);
assert.deepEqual(functionNames, [
"run",
"run3",
"run2",
"run1",
"runRequire"
]);
contractNames = report.map(({ contractName }) => contractName);
assert(contractNames.every(name => name === "StacktraceTest"));
status = report[report.length - 1].status;
assert.isUndefined(status);
location = report[report.length - 1].location;
Expand Down Expand Up @@ -225,15 +243,17 @@ describe("Stack tracing", function() {
await session.continueUntilBreakpoint(); //run till end

let report = session.view(stacktrace.current.finalReport);
let names = report.map(({ name }) => name);
assert.deepEqual(names, [
let functionNames = report.map(({ functionName }) => functionName);
assert.deepEqual(functionNames, [
"run",
"run3",
"run2",
"run1",
"runPay",
undefined
]);
let contractNames = report.map(({ contractName }) => contractName);
assert(contractNames.every(name => name === "StacktraceTest"));
let status = report[report.length - 1].status;
assert.isFalse(status);
let location = report[report.length - 2].location; //note, -2 because of undefined on top
Expand Down Expand Up @@ -266,15 +286,19 @@ describe("Stack tracing", function() {
await session.continueUntilBreakpoint(); //run till end

let report = session.view(stacktrace.current.finalReport);
let names = report.map(({ name }) => name);
assert.deepEqual(names, [
let functionNames = report.map(({ functionName }) => functionName);
assert.deepEqual(functionNames, [
"run",
"run3",
"run2",
"run1",
"runInternal",
undefined
]);
let contractNames = report.map(({ contractName }) => contractName);
assert.isUndefined(contractNames[contractNames.length - 1]);
contractNames.pop();
assert(contractNames.every(name => name === "StacktraceTest"));
let status = report[report.length - 1].status;
assert.isFalse(status);
let location = report[report.length - 2].location; //note, -2 because of undefined on top
Expand Down Expand Up @@ -308,8 +332,19 @@ describe("Stack tracing", function() {
await session.continueUntilBreakpoint(); //run till end

let report = session.view(stacktrace.current.finalReport);
let names = report.map(({ name }) => name);
assert.deepEqual(names, ["run", "run3", "run2", "run1", "runBoom", "boom"]);
let functionNames = report.map(({ functionName }) => functionName);
assert.deepEqual(functionNames, [
"run",
"run3",
"run2",
"run1",
"runBoom",
"boom"
]);
let contractNames = report.map(({ contractName }) => contractName);
assert.strictEqual(contractNames[contractNames.length - 1], "Boom");
contractNames.pop();
assert(contractNames.every(name => name === "StacktraceTest"));
let status = report[report.length - 1].status;
assert.isTrue(status);
let location = report[report.length - 1].location;
Expand Down

0 comments on commit 19c7ea2

Please sign in to comment.