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

Commit

Permalink
Merge pull request #670 from Gozala/bug/stack@551604
Browse files Browse the repository at this point in the history
Bug 551604 - Improve stack traces from modules r=@erikvold
  • Loading branch information
Gozala committed Dec 4, 2012
2 parents 2dbe714 + 039c631 commit 87aa0bf
Show file tree
Hide file tree
Showing 17 changed files with 311 additions and 76 deletions.
8 changes: 6 additions & 2 deletions lib/sdk/console/plain-text.js
Expand Up @@ -8,8 +8,9 @@ module.metadata = {
"stability": "unstable"
};

const {Cc,Ci} = require("chrome");
const { Cc, Ci } = require("chrome");
const self = require("../self");
const { sourceURI } = require("./traceback")

function stringify(arg) {
try {
Expand Down Expand Up @@ -75,7 +76,10 @@ Console.prototype = {

exception: function exception(e) {
var fullString = ("An exception occurred.\n" +
require("./traceback").format(e) + "\n" + e);
e.name + ": " + e.message + "\n" +
sourceURI(e.fileName) + " " +
e.lineNumber + "\n" +
require("./traceback").format(e));
this.error(fullString);
},

Expand Down
61 changes: 16 additions & 45 deletions lib/sdk/console/traceback.js
Expand Up @@ -9,12 +9,10 @@ module.metadata = {
};

const { Cc, Ci, components } = require("chrome");
const { parseStack, sourceURI } = require("toolkit/loader");
const { readURISync } = require("../net/url");

// Undo the auto-parentification of URLs done in bug 418356.
function deParentifyURL(url) {
return url ? url.split(" -> ").slice(-1)[0] : url;
}
exports.sourceURI = sourceURI

function safeGetFileLine(path, line) {
try {
Expand All @@ -27,43 +25,16 @@ function safeGetFileLine(path, line) {
return null;
}

function errorStackToJSON(stack) {
var lines = stack.split("\n");

var frames = [];
lines.forEach(
function(line) {
if (!line)
return;
var atIndex = line.indexOf("@");
var colonIndex = line.lastIndexOf(":");
var filename = deParentifyURL(line.slice(atIndex + 1, colonIndex));
var lineNo = parseInt(line.slice(colonIndex + 1));
var funcSig = line.slice(0, atIndex);
var endFuncName = funcSig.indexOf("(");
// Bug 751149: FF15 changed function signature
// Instead of: runTest([object Object])
// We now have: runTest
var funcName = endFuncName != -1
? funcSig.slice(0, endFuncName)
: funcSig;
frames.unshift({filename: filename,
funcName: funcName,
lineNo: lineNo});
});

return frames;
};

function nsIStackFramesToJSON(frame) {
var stack = [];

while (frame) {
if (frame.filename) {
var filename = deParentifyURL(frame.filename);
stack.splice(0, 0, {filename: filename,
lineNo: frame.lineNumber,
funcName: frame.name});
stack.push({
fileName: sourceURI(frame.filename),
lineNumber: frame.lineNumber,
name: frame.name
});
}
frame = frame.caller;
}
Expand All @@ -75,11 +46,11 @@ var fromException = exports.fromException = function fromException(e) {
if (e instanceof Ci.nsIException)
return nsIStackFramesToJSON(e.location);
if (e.stack && e.stack.length)
return errorStackToJSON(e.stack);
return parseStack(e.stack);
if (e.fileName && typeof(e.lineNumber == "number"))
return [{filename: deParentifyURL(e.fileName),
lineNo: e.lineNumber,
funcName: null}];
return [{fileName: sourceURI(e.fileName),
lineNumber: e.lineNumber,
name: null}];
return [];
};

Expand All @@ -90,7 +61,7 @@ var get = exports.get = function get() {
var format = exports.format = function format(tbOrException) {
if (tbOrException === undefined) {
tbOrException = get();
tbOrException.splice(-1, 1);
tbOrException.splice(0, 1);
}

var tb;
Expand All @@ -104,12 +75,12 @@ var format = exports.format = function format(tbOrException) {

tb.forEach(
function(frame) {
if (!(frame.filename || frame.lineNo || frame.funcName))
if (!(frame.fileName || frame.lineNumber || frame.name))
return;

lines.push(' File "' + frame.filename + '", line ' +
frame.lineNo + ', in ' + frame.funcName);
var sourceLine = safeGetFileLine(frame.filename, frame.lineNo);
lines.push(' File "' + frame.fileName + '", line ' +
frame.lineNumber + ', in ' + frame.name);
var sourceLine = safeGetFileLine(frame.fileName, frame.lineNumber);
if (sourceLine)
lines.push(' ' + sourceLine.trim());
});
Expand Down
13 changes: 12 additions & 1 deletion lib/sdk/test/harness.js
Expand Up @@ -10,6 +10,7 @@ module.metadata = {

const { Cc,Ci } = require("chrome");
const { Loader } = require('./loader');
const { serializeStack, parseStack } = require("toolkit/loader");
const { setTimeout } = require('../timers');
const memory = require('../deprecated/memory');
const { PlainTextConsole } = require("../console/plain-text");
Expand Down Expand Up @@ -320,7 +321,17 @@ var runTests = exports.runTests = function runTests(options) {

nextIteration();
} catch (e) {
print(format(e) + "\n" + e + "\n");
let frames = parseStack(e.stack).reverse().reduce(function(frames, frame) {
if (frame.fileName.split("/").pop() === "unit-test-finder.js")
frames.done = true
if (!frames.done) frames.push(frame)

return frames
}, [])

e.stack = serializeStack(frames)

print("Error: " + e + " \n " + format(e));
onDone({passed: 0, failed: 1});
}
};
Expand Down
6 changes: 3 additions & 3 deletions lib/sdk/util/deprecate.js
Expand Up @@ -8,13 +8,13 @@ module.metadata = {
"stability": "experimental"
};

const traceback = require("../console/traceback");
const { get, format } = require("../console/traceback");

function deprecateUsage(msg) {
// Print caller stacktrace in order to help figuring out which code
// does use deprecated thing
let stack = traceback.get().slice(0, -2);
console.error("DEPRECATED: " + msg + "\n" + traceback.format(stack));
let stack = get().slice(2);
console.error("DEPRECATED: " + msg + "\n" + format(stack));
}
exports.deprecateUsage = deprecateUsage;

Expand Down
57 changes: 56 additions & 1 deletion lib/toolkit/loader.js
Expand Up @@ -107,6 +107,42 @@ const override = iced(function override(target, source) {
});
exports.override = override;


function sourceURI(uri) { return String(uri).split(" -> ").pop(); }
exports.sourceURI = iced(sourceURI);

function isntLoaderFrame(frame) { return frame.fileName !== module.uri }

var parseStack = iced(function parseStack(stack) {
let lines = String(stack).split("\n");
return lines.reduce(function(frames, line) {
if (line) {
let atIndex = line.indexOf("@");
let columnIndex = line.lastIndexOf(":");
let fileName = sourceURI(line.slice(atIndex + 1, columnIndex));
let lineNumber = parseInt(line.slice(columnIndex + 1));
let name = line.slice(0, atIndex).split("(").shift();
frames.unshift({
fileName: fileName,
name: name,
lineNumber: lineNumber
});
}
return frames;
}, []);
})
exports.parseStack = parseStack

var serializeStack = iced(function serializeStack(frames) {
return frames.reduce(function(stack, frame) {
return frame.name + "@" +
frame.fileName + ":" +
frame.lineNumber + "\n" +
stack;
}, "");
})
exports.serializeStack = serializeStack

// Function takes set of options and returns a JS sandbox. Function may be
// passed set of options:
// - `name`: A string value which identifies the sandbox in about:memory. Will
Expand Down Expand Up @@ -197,7 +233,26 @@ const load = iced(function load(loader, module) {
wantXrays: false
});

evaluate(sandbox, module.uri);
try {
evaluate(sandbox, module.uri);
} catch (error) {
let stack = error.stack;
let frames = parseStack(stack).filter(isntLoaderFrame);

// Note that `String(error)` where error is from subscript loader does
// not puts `:` after `"Error"` unlike regular errors thrown by JS code.
if (String(error) === "Error opening input stream (invalid filename?)") {
frames = parseStack(Error().stack).filter(isntLoaderFrame);
let caller = frames.slice(0).pop();
error = Error("Module `" + module.id + "` is not found at " + module.uri,
caller.fileName,
caller.lineNumber);
}

error.stack = serializeStack(frames);

throw error
}

if (module.exports && typeof(module.exports) === 'object')
freeze(module.exports);
Expand Down
7 changes: 7 additions & 0 deletions test/fixtures/loader/errors/boomer.js
@@ -0,0 +1,7 @@
/* 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";

throw Error("opening input stream (invalid filename?)");
9 changes: 9 additions & 0 deletions test/fixtures/loader/errors/main.js
@@ -0,0 +1,9 @@
/* 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';

var error = require('./boomer');

exports.main = exports;
9 changes: 9 additions & 0 deletions test/fixtures/loader/exceptions/boomer.js
@@ -0,0 +1,9 @@
/* 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';

exports.boom = function() {
throw Error("Boom!");
};
11 changes: 11 additions & 0 deletions test/fixtures/loader/exceptions/main.js
@@ -0,0 +1,11 @@
/* 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';

var boomer = require('./boomer');

boomer.boom();

exports.main = exports;
10 changes: 10 additions & 0 deletions test/fixtures/loader/missing/main.js
@@ -0,0 +1,10 @@
/* 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';

var a = require('./not-found');

exports.a = a;
exports.main = exports;
7 changes: 7 additions & 0 deletions test/fixtures/loader/syntax-error/error.js
@@ -0,0 +1,7 @@
/* 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';

exports.b = @ require('b');
10 changes: 10 additions & 0 deletions test/fixtures/loader/syntax-error/main.js
@@ -0,0 +1,10 @@
/* 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';

var a = require('./error');

exports.a = a;
exports.main = exports;
1 change: 1 addition & 0 deletions test/test-deprecate.js
Expand Up @@ -34,6 +34,7 @@ exports["test Deprecate Usage"] = function testDeprecateUsage(assert) {
assert.equal(errors.length, 1, "only one error is dispatched");

let msg = errors[0];

assert.ok(msg.indexOf("foo") !== -1,
"message contains the given message");
assert.ok(msg.indexOf("functionIsDeprecated") !== -1,
Expand Down

0 comments on commit 87aa0bf

Please sign in to comment.