зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1160691 - Optimize FrameUtils.isContent and FrameUtils.parseLocation. (r=jsantell)
This commit is contained in:
Родитель
3390ef56f7
Коммит
efa9b671f2
|
@ -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;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче