Bug 1160691 - Optimize FrameUtils.isContent and FrameUtils.parseLocation. (r=jsantell)

This commit is contained in:
Shu-yu Guo 2015-05-11 14:16:44 -07:00
Родитель 3390ef56f7
Коммит efa9b671f2
1 изменённых файлов: 221 добавлений и 44 удалений

Просмотреть файл

@ -9,37 +9,122 @@ loader.lazyRequireGetter(this, "Services");
loader.lazyRequireGetter(this, "CATEGORY_OTHER",
"devtools/shared/profiler/global", true);
// Character codes used in various parsing helper functions.
const CHAR_CODE_A = "a".charCodeAt(0);
const CHAR_CODE_C = "c".charCodeAt(0);
const CHAR_CODE_E = "e".charCodeAt(0);
const CHAR_CODE_F = "f".charCodeAt(0);
const CHAR_CODE_H = "h".charCodeAt(0);
const CHAR_CODE_I = "i".charCodeAt(0);
const CHAR_CODE_J = "j".charCodeAt(0);
const CHAR_CODE_L = "l".charCodeAt(0);
const CHAR_CODE_M = "m".charCodeAt(0);
const CHAR_CODE_O = "o".charCodeAt(0);
const CHAR_CODE_P = "p".charCodeAt(0);
const CHAR_CODE_R = "r".charCodeAt(0);
const CHAR_CODE_S = "s".charCodeAt(0);
const CHAR_CODE_T = "t".charCodeAt(0);
const CHAR_CODE_U = "u".charCodeAt(0);
const CHAR_CODE_0 = "0".charCodeAt(0);
const CHAR_CODE_9 = "9".charCodeAt(0);
const CHAR_CODE_LPAREN = "(".charCodeAt(0);
const CHAR_CODE_COLON = ":".charCodeAt(0);
const CHAR_CODE_SLASH = "/".charCodeAt(0);
// The cache used in the `nsIURL` function.
const gNSURLStore = new Map();
const CHROME_SCHEMES = ["chrome://", "resource://", "jar:file://"];
const CONTENT_SCHEMES = ["http://", "https://", "file://", "app://"];
/**
* Parses the raw location of this function call to retrieve the actual
* function name, source url, host name, line and column.
*/
exports.parseLocation = function parseLocation (frame) {
exports.parseLocation = function parseLocation(location, fallbackLine, fallbackColumn) {
// Parse the `location` for the function name, source url, line, column etc.
let lineAndColumn = frame.location.match(/((:\d+)*)\)?$/)[1];
let [, line, column] = lineAndColumn.split(":");
line = line || frame.line;
column = column || frame.column;
let firstParenIndex = frame.location.indexOf("(");
let lineAndColumnIndex = frame.location.indexOf(lineAndColumn);
let resource = frame.location.substring(firstParenIndex + 1, lineAndColumnIndex);
let line, column, url;
// These two indices are used to extract the resource substring, which is
// location[firstParenIndex + 1 .. lineAndColumnIndex].
//
// The resource substring is extracted iff a line number was found. There
// may be no parentheses, in which case the substring starts at 0.
//
// For example, take "foo (bar.js:1)".
// ^ ^
// | -----+
// +-------+ |
// | |
// firstParenIndex will point to -+ |
// |
// lineAndColumnIndex will point to --+
//
// For an example without parentheses, take "bar.js:2".
// ^ ^
// | |
// firstParenIndex will point to -----------+ |
// |
// lineAndColumIndex will point to ----------------+
let firstParenIndex = -1;
let lineAndColumnIndex = -1;
// Compute firstParenIndex and lineAndColumnIndex. If lineAndColumnIndex is
// found, also extract the line and column.
for (let i = 0; i < location.length; i++) {
let c = location.charCodeAt(i);
// The url and line information might be inside parentheses.
if (c === CHAR_CODE_LPAREN) {
if (firstParenIndex < 0) {
firstParenIndex = i;
}
continue;
}
// Look for numbers after colons, twice. Firstly for the line, secondly
// for the column.
if (c === CHAR_CODE_COLON) {
if (isNumeric(location.charCodeAt(i + 1))) {
// If we found a line number, remember when it starts.
if (lineAndColumnIndex < 0) {
lineAndColumnIndex = i;
}
let start = ++i;
let length = 1;
while (isNumeric(location.charCodeAt(++i))) {
length++;
}
if (!line) {
line = location.substr(start, length);
} else if (!column) {
column = location.substr(start, length);
}
break;
}
}
}
let uri;
if (lineAndColumnIndex > 0) {
let resource = location.substring(firstParenIndex + 1, lineAndColumnIndex);
url = resource.split(" -> ").pop();
if (url) {
uri = nsIURL(url);
}
}
let url = resource.split(" -> ").pop();
let uri = nsIURL(url);
let functionName, fileName, hostName;
// If the URI digged out from the `location` is valid, this is a JS frame.
if (uri) {
functionName = frame.location.substring(0, firstParenIndex - 1);
functionName = location.substring(0, firstParenIndex - 1);
fileName = (uri.fileName + (uri.ref ? "#" + uri.ref : "")) || "/";
hostName = getHost(url, uri.host);
} else {
functionName = frame.location;
functionName = location;
url = null;
}
@ -51,37 +136,38 @@ exports.parseLocation = function parseLocation (frame) {
line: line,
column: column
};
},
/**
* Determines if the given frame is the (root) frame.
*
* @param object frame
* @return boolean
*/
exports.isRoot = function isRoot({ location }) {
return location === "(root)";
};
/**
* Checks if the specified function represents a chrome or content frame.
*
* @param object frame
* The { category, location } properties of the frame.
* @return boolean
* True if a content frame, false if a chrome frame.
*/
exports.isContent = function isContent (frame) {
if (exports.isRoot(frame)) {
return true;
* Checks if the specified function represents a chrome or content frame.
*
* @param string location
* The location of the frame.
* @param number category [optional]
* If a chrome frame, the category.
* @return boolean
* True if a content frame, false if a chrome frame.
*/
function isContent({ location, category }) {
// Only C++ stack frames have associated category information.
if (category) {
return false;
}
// Only C++ stack frames have associated category information.
const { category, location } = frame;
return !!(!category &&
!CHROME_SCHEMES.find(e => location.includes(e)) &&
CONTENT_SCHEMES.find(e => location.includes(e)));
// Locations in frames with function names look like:
// "functionName (foo://bar)".
// Look for the starting left parenthesis, then try to match a
// scheme name.
for (let i = 0; i < location.length; i++) {
if (location.charCodeAt(i) === CHAR_CODE_LPAREN) {
return isContentScheme(location, i + 1);
}
}
// If there was no left parenthesis, try matching from the start.
return isContentScheme(location, 0);
}
exports.isContent = isContent;
/**
* This filters out platform data frames in a sample. With latest performance
@ -149,15 +235,106 @@ function nsIURL(url) {
}
gNSURLStore.set(url, uri);
return uri;
}
};
/**
* Takes a `host` string from an nsIURL instance and
* returns the same string, or null, if it's an invalid host.
*/
function getHost (url, hostName) {
if (CHROME_SCHEMES.find(e => url.indexOf(e) === 0)) {
return null;
}
return hostName;
return isChromeScheme(url) ? null : hostName;
}
// For the functions below, we assume that we will never access the location
// argument out of bounds, which is indeed the vast majority of cases.
//
// They are written this way because they are hot. Each frame is checked for
// being content or chrome when processing the profile.
function isColonSlashSlash(location, i) {
return location.charCodeAt(++i) === CHAR_CODE_COLON &&
location.charCodeAt(++i) === CHAR_CODE_SLASH &&
location.charCodeAt(++i) === CHAR_CODE_SLASH;
}
function isContentScheme(location, i) {
let firstChar = location.charCodeAt(i);
switch (firstChar) {
case CHAR_CODE_H: // "http://" or "https://"
if (location.charCodeAt(++i) === CHAR_CODE_T &&
location.charCodeAt(++i) === CHAR_CODE_T &&
location.charCodeAt(++i) === CHAR_CODE_P) {
if (location.charCodeAt(i) === CHAR_CODE_S) {
++i;
}
return isColonSlashSlash(location, i);
}
return false;
case CHAR_CODE_F: // "file://"
if (location.charCodeAt(++i) === CHAR_CODE_I &&
location.charCodeAt(++i) === CHAR_CODE_L &&
location.charCodeAt(++i) === CHAR_CODE_E) {
return isColonSlashSlash(location, i);
}
return false;
case CHAR_CODE_A: // "app://"
if (location.charCodeAt(++i) == CHAR_CODE_P &&
location.charCodeAt(++i) == CHAR_CODE_P) {
return isColonSlashSlash(location, i);
}
return false;
default:
return false;
}
}
function isChromeScheme(location, i) {
let firstChar = location.charCodeAt(i);
switch (firstChar) {
case CHAR_CODE_C: // "chrome://"
if (location.charCodeAt(++i) === CHAR_CODE_H &&
location.charCodeAt(++i) === CHAR_CODE_R &&
location.charCodeAt(++i) === CHAR_CODE_O &&
location.charCodeAt(++i) === CHAR_CODE_M &&
location.charCodeAt(++i) === CHAR_CODE_E) {
return isColonSlashSlash(location, i);
}
return false;
case CHAR_CODE_R: // "resource://"
if (location.charCodeAt(++i) === CHAR_CODE_E &&
location.charCodeAt(++i) === CHAR_CODE_S &&
location.charCodeAt(++i) === CHAR_CODE_O &&
location.charCodeAt(++i) === CHAR_CODE_U &&
location.charCodeAt(++i) === CHAR_CODE_R &&
location.charCodeAt(++i) === CHAR_CODE_C &&
location.charCodeAt(++i) === CHAR_CODE_E) {
return isColonSlashSlash(location, i);
}
return false;
case CHAR_CODE_J: // "jar:file://"
if (location.charCodeAt(++i) === CHAR_CODE_A &&
location.charCodeAt(++i) === CHAR_CODE_R &&
location.charCodeAt(++i) === CHAR_CODE_COLON &&
location.charCodeAt(++i) === CHAR_CODE_F &&
location.charCodeAt(++i) === CHAR_CODE_I &&
location.charCodeAt(++i) === CHAR_CODE_L &&
location.charCodeAt(++i) === CHAR_CODE_E) {
return isColonSlashSlash(location, i);
}
return false;
default:
return false;
}
}
function isNumeric(c) {
return c >= CHAR_CODE_0 && c <= CHAR_CODE_9;
}