37#define __block __glibc_block
47#define OOJS_DEBUG_LIMITER 1
49#define OOJS_DEBUG_LIMITER 0
61#define OOJS_TIME_LIMIT (0.2)
63#define OOJS_TIME_LIMIT (1)
76#if OOJS_PROFILE && defined(MOZ_TRACE_JSCALLS)
77static void FunctionCallback(JSFunction *
function, JSScript *script, JSContext *context,
int entering);
116 OOLog(
@"bug.javaScript.limiterDepth",
@"Attempt to stop JavaScript time limiter while it is already fully stopped. This is an internal bug, please report it. (Last start: %@:%u, last valid stop: %@:%u, this stop attempt: %@:%u.)",
OOLogAbbreviatedFileName(
sLastStartedFile),
sLastStartedLine,
OOLogAbbreviatedFileName(
sLastStoppedFile),
sLastStoppedLine,
OOLogAbbreviatedFileName(file), line);
123#if OOJS_DEBUG_LIMITER
186@implementation OOJavaScriptEngine (WatchdogTimer)
209 JS_TriggerAllOperationCallbacks(_runtime);
219 if (!
sStop)
return YES;
221 JS_ClearPendingException(context);
229 OOLogERR(
@"script.javaScript.timeLimit",
@"Script \"%@\
" ran for %g seconds and has been terminated.", [[
OOJSScript currentlyRunningScript] name], elapsed);
242 if (contextOp == JSCONTEXT_NEW)
246#if OOJS_PROFILE && defined(MOZ_TRACE_JSCALLS)
247 JS_SetFunctionCallback(context, (JSFunctionCallback)FunctionCallback);
256 [NSThread detachNewThreadSelector:@selector(watchdogTimerThread)
266#ifndef MOZ_TRACE_JSCALLS
267#warning Profiling is enabled, but MOZ_TRACE_JSCALLS is disabled, so only native functions will be profiled.
281@interface OOTimeProfile (Private)
283- (void) setTotalTime:(
double)value;
284- (void) setNativeTime:(
double)value;
285#ifdef MOZ_TRACE_JSCALLS
286- (void) setJavaScriptTime:(
double)value;
288- (void) setProfilerOverhead:(
double)value;
289- (void) setExtensionTime:(
double)value;
290- (void) setProfileEntries:(NSArray *)value;
292- (NSDictionary *) propertyListRepresentation;
297@interface OOTimeProfileEntry (Private)
299- (id) initWithCName:(const
char *)name;
300#ifdef MOZ_TRACE_JSCALLS
301- (id) initWithJSFunction:(JSFunction *)function context:(JSContext *)context;
306- (NSDictionary *) propertyListRepresentation;
316 sProfileInfo = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks, NSObjectMapValueCallBacks, 100);
327 OOLog(
@"script.javaScript.trace",
@"%@",
@">>>> Beginning trace.");
346 [result
setTotalTime:OOHighResTimeDeltaInSeconds(sProfilerStartTime, now)];
348#ifdef MOZ_TRACE_JSCALLS
349 [result setJavaScriptTime:sProfilerTotalJavaScriptTime];
356 [result
setProfileEntries:[NSAllMapTableValues(sProfileInfo) sortedArrayUsingSelector:@selector(compareBySelfTimeReverse:)]];
361 OOLog(
@"script.javaScript.trace",
@"%@",
@"<<<< End of trace.");
389#ifdef MOZ_TRACE_JSCALLS
390static void CleanUpJSFrame(OOJSProfileStackFrame *frame)
396static void TraceEnterJSFunction(JSContext *context, JSFunction *
function,
OOTimeProfileEntry *profileEntry)
398 NSMutableString *name = [NSMutableString stringWithFormat:@"%@(", [profileEntry
function]];
399 BOOL isNative = JS_GetFunctionNative(context,
function) != NULL;
400 NSString *frameTag =
nil;
401 NSString *logMsgClass =
nil;
406 JSStackFrame *frame = NULL;
410 JSPropertyDescArray properties = { 0 , NULL };
416 if (JS_FrameIterator(context, &frame) != NULL)
418 if (JS_IsConstructorFrame(context, frame))
420 [name insertString:@"new " atIndex:0];
423 if (JS_GetFrameThis(context, frame, &
this))
425 [name appendFormat:@"this: %@", OOJSDescribeValue(context, this, YES)];
429 scope = JS_GetFrameScopeChain(context, frame);
430 if (scope != NULL && JS_GetPropertyDescArray(context, scope, &properties))
432 for (i = 0; i < properties.length; i++)
434 JSPropertyDesc *prop = &properties.array[i];
435 if (prop->flags & JSPD_ARGUMENT)
437 if (!first) [name appendFormat:
@", "];
440 [name appendFormat:
@"%@: %@", OOStringFromJSValueEvenIfNull(context, prop->id), OOJSDescribeValue(context, prop->value, YES)];
449 logMsgClass =
@"script.javaScript.trace.JS";
454 logMsgClass =
@"script.javaScript.trace.NW";
457 [name appendString:@")"];
458 OOLog(logMsgClass,
@">> %@ [%@]", name, frameTag);
463static void FunctionCallback(JSFunction *
function, JSScript *script, JSContext *context,
int entering)
469 if (!
sTracing && JS_GetFunctionNative(context,
function) != NULL)
return;
473 NSAutoreleasePool *pool = [NSAutoreleasePool new];
489 TraceEnterJSFunction(context,
function, entry);
493 OOJSProfileStackFrame *frame = malloc(
sizeof(OOJSProfileStackFrame));
494 assert(frame != NULL);
496 *frame = (OOJSProfileStackFrame)
503 .cleanup = CleanUpJSFrame
533 OOLog(
@"script.javaScript.trace.ON",
@">> %s [ON]",
function);
537 *frame = (OOJSProfileStackFrame)
541 .function =
function,
554 NSAutoreleasePool *pool = [NSAutoreleasePool new];
599 NSMapInsertKnownAbsent(
sProfileInfo, frame->key, entry);
607 *(frame->total) += selfTime;
610 if (frame->cleanup != NULL) frame->cleanup(frame);
626- (NSString *) description
628 double totalTime = [
self totalTime];
630 NSMutableString *result = [NSMutableString stringWithFormat:
631 @"Total time: %g ms\n"
632 "JavaScript: %g ms, native: %g ms\n"
633 "Counted towards limit: %g ms, excluded: %g ms\n"
634 "Profiler overhead: %g ms",
636 [
self javaScriptTime] * 1000.0, [
self nativeTime] * 1000.0,
637 [
self nonExtensionTime] * 1000.0, [
self extensionTime] * 1000.0,
638 [
self profilerOverhead] * 1000.0];
640 NSArray *profileEntries = [
self profileEntries];
641 NSUInteger i,
count = [profileEntries count];
644 [result appendString:@"\n NAME T COUNT TOTAL SELF TOTAL% SELF% SELFMAX"];
645 for (i = 0; i <
count; i++)
651 double totalPc = [entry
totalTimeSum] * 100.0 / totalTime;
652 double selfPc = [entry
selfTimeSum] * 100.0 / totalTime;
654 [result appendFormat:@"\n%60s %c%7lu %8.2f %8.2f %5.1f %5.1f %8.2f",
671- (void) setTotalTime:(
double)value
677- (double) javaScriptTime
679#ifdef MOZ_TRACE_JSCALLS
680 return _javaScriptTime;
682 return _totalTime - _nativeTime;
687#ifdef MOZ_TRACE_JSCALLS
688- (void) setJavaScriptTime:(
double)value
690 _javaScriptTime = value;
701- (void) setNativeTime:(
double)value
707- (double) extensionTime
709 return _extensionTime;
713- (void) setExtensionTime:(
double)value
715 _extensionTime = value;
719- (double) nonExtensionTime
721 return _totalTime - _extensionTime;
725- (double) profilerOverhead
727 return _profilerOverhead;
731- (void) setProfilerOverhead:(
double)value
733 _profilerOverhead = value;
737- (NSArray *) profileEntries
739 return _profileEntries;
743- (void) setProfileEntries:(NSArray *)value
745 if (_profileEntries != value)
747 [_profileEntries release];
748 _profileEntries = [value retain];
753- (jsval) oo_jsValueInContext:(JSContext *)context
759- (NSDictionary *) propertyListRepresentation
761 NSArray *profileEntries = [
self profileEntries];
762 NSMutableArray *convertedEntries = [NSMutableArray arrayWithCapacity:[profileEntries count]];
763 NSEnumerator *entryEnum =
nil;
765 for (entryEnum = [profileEntries objectEnumerator]; (entry = [entryEnum nextObject]); )
770 return [NSDictionary dictionaryWithObjectsAndKeys:
771 profileEntries, @"profiles",
772 [NSNumber numberWithDouble:[
self totalTime]], @"totalTime",
773 [NSNumber numberWithDouble:[
self javaScriptTime]], @"javaScriptTime",
774 [NSNumber numberWithDouble:[
self nativeTime]], @"nativeTime",
775 [NSNumber numberWithDouble:[
self extensionTime]], @"extensionTime",
776 [NSNumber numberWithDouble:[
self nonExtensionTime]], @"nonExtensionTime",
777 [NSNumber numberWithDouble:[
self profilerOverhead]], @"profilerOverhead",
786- (id) initWithCName:(const
char *)name
788 NSAssert(
sProfiling,
@"Can't create profile entries while not profiling.");
790 if ((
self = [super init]))
794 _function = [[NSString stringWithUTF8String:name] retain];
803- (id) initWithJSFunction:(JSFunction *)function context:(JSContext *)context
805 if ((
self = [
self initWithCName:NULL]))
809 _jsFunction =
function;
811 NSString *funcName =
nil;
812 JSString *jsName = JS_GetFunctionId(_jsFunction);
813 if (jsName != NULL) funcName = [OOStringFromJSString(context, jsName) retain];
814 else funcName =
@"<anonymous>";
817 NSString *location =
nil;
818 if (JS_GetFunctionNative(context,
function) == NULL)
820 JSStackFrame *frame = NULL;
821 if (JS_FrameIterator(context, &frame) != NULL)
829 _function = [[NSString alloc] initWithFormat:@"(%@) %@", location, funcName];
831 else _function = [funcName retain];
852 _totalTimeSum += totalTime;
853 _selfTimeSum += selfTime;
854 _totalTimeMax = fmax(_totalTimeMax, totalTime);
855 _selfTimeMax = fmax(_selfTimeMax, selfTime);
859- (NSString *) description
861 if (_hitCount == 0)
return [NSString stringWithFormat:@"%@: --", _function];
864 float totalTimeSum = _totalTimeSum * 1000.0;
865 float selfTimeSum = _selfTimeSum * 1000.0;
866 float totalTimeMax = _totalTimeMax * 1000.0;
867 float selfTimeMax = _selfTimeMax * 1000.0;
869 if (totalTimeSum == selfTimeSum && totalTimeMax == selfTimeMax)
873 return [NSString stringWithFormat:@"%@: 1 time, %g ms", _function, totalTimeSum];
877 return [NSString stringWithFormat:@"%@: %lu times, total %g ms, avg %g ms, max %g ms", _function, _hitCount, totalTimeSum, totalTimeSum / _hitCount, totalTimeMax];
884 return [NSString stringWithFormat:@"%@: 1 time, %g ms (self %g ms)", _function, totalTimeSum, selfTimeSum];
888 return [NSString stringWithFormat:@"%@: %lu times, total %g ms (self %g ms), avg %g ms (self %g ms), max %g ms, max self %g ms", _function, _hitCount, totalTimeSum, selfTimeSum, totalTimeSum / _hitCount, selfTimeSum / _hitCount, totalTimeMax, selfTimeMax];
894- (NSString *) function
900- (NSUInteger) hitCount
906- (double) totalTimeSum
908 return _totalTimeSum;
912- (double) selfTimeSum
918- (double) totalTimeAverage
920 return _hitCount ? (_totalTimeSum / _hitCount) : 0.0;
924- (double) selfTimeAverage
926 return _hitCount ? (_selfTimeSum / _hitCount) : 0.0;
930- (double) totalTimeMax
932 return _totalTimeMax;
936- (double) selfTimeMax
942- (BOOL) isJavaScriptFrame
945 return _jsFunction != NULL;
954 return -[
self compareByTotalTimeReverse:other];
960 double selfTotal = [
self totalTimeSum];
963 if (selfTotal < otherTotal)
return NSOrderedDescending;
964 if (selfTotal > otherTotal)
return NSOrderedAscending;
965 return NSOrderedSame;
971 return -[
self compareBySelfTimeReverse:other];
977 double selfTotal = [
self selfTimeSum];
980 if (selfTotal < otherTotal)
return NSOrderedDescending;
981 if (selfTotal > otherTotal)
return NSOrderedAscending;
982 return NSOrderedSame;
986- (jsval) oo_jsValueInContext:(JSContext *)context
992- (NSDictionary *) propertyListRepresentation
994 return [NSDictionary dictionaryWithObjectsAndKeys:
996 [NSNumber numberWithUnsignedInteger:[
self hitCount]], @"hitCount",
997 [NSNumber numberWithDouble:[
self totalTimeSum]], @"totalTimeSum",
998 [NSNumber numberWithDouble:[
self selfTimeSum]], @"selfTimeSum",
999 [NSNumber numberWithDouble:[
self totalTimeAverage]], @"totalTimeAverage",
1000 [NSNumber numberWithDouble:[
self selfTimeAverage]], @"selfTimeAverage",
1001 [NSNumber numberWithDouble:[
self totalTimeMax]], @"totalTimeMax",
1002 [NSNumber numberWithDouble:[
self selfTimeMax]], @"selfTimeMax",
1003 [NSNumber numberWithBool:[
self isJavaScriptFrame]], @"isJavaScriptFrame",
#define OOJSProfileExit(frame)
#define OOJSProfileEnter(frame, function)
OOTimeProfile * OOJSEndProfiling(void)
#define OOJSStopTimeLimiter()
BOOL OOJSIsProfiling(void)
void OOJSBeginProfiling(BOOL trace)
OOTimeDelta OOJSGetTimeLimiterLimit(void)
void OOJSTimeManagementInit(OOJavaScriptEngine *engine, JSRuntime *runtime)
#define OOJSStartTimeLimiterWithTimeLimit(limit)
void OOJSPauseTimeLimiter(void)
static OOJSProfileStackFrame * sProfileStack
void OOJSBeginTracing(void)
void OOJSResetTimeLimiter(void)
static void UpdateProfileForFrame(OOHighResTimeValue now, OOJSProfileStackFrame *frame)
static double sProfilerTotalNativeTime
static unsigned sLastStoppedLine
static const char * sLastStoppedFile
static OOHighResTimeValue sProfilerStartTime
static double sProfilerOverhead
static JSBool OperationCallback(JSContext *context)
static const char * sLastStartedFile
void OOJSEndTracing(void)
OOHighResTimeValue OOJSCopyTimeLimiterNominalStartTime(void)
static OOHighResTimeValue sLimiterPauseStart
static double sLimiterTimeLimit
OOTimeDelta OOJSGetTimeLimiterLimit(void)
static double sProfilerTotalJavaScriptTime
static int sLimiterPauseDepth
static double sProfilerEntryTimeLimit
void OOJSStartTimeLimiterWithTimeLimit_(OOTimeDelta limit, const char *file, unsigned line)
static unsigned sLimiterStartDepth
void OOJSStopTimeLimiter_(const char *file, unsigned line)
void OOJSResumeTimeLimiter(void)
static OOHighResTimeValue sLimiterStart
static unsigned sLastStartedLine
static JSBool ContextCallback(JSContext *context, uintN contextOp)
static NSMapTable * sProfileInfo
void OOJSSetTimeLimiterLimit(OOTimeDelta limit)
void OOJSPauseTimeLimiter(void)
OOINLINE jsval OOJSValueFromNativeObject(JSContext *context, id object)
void OOJSResumeTimeLimiter(void)
NSString * OOJSDescribeLocation(JSContext *context, JSStackFrame *stackFrame)
void OOJSDumpStack(JSContext *context)
#define OOLogERR(class, format,...)
#define OOLog(class, format,...)
NSString * OOLogAbbreviatedFileName(const char *inName)
OOTimeDelta OOHighResTimeDeltaInSeconds(OOHighResTimeValue startTime, OOHighResTimeValue endTime)
uint64_t OOHighResTimeValue
#define OODisposeHighResTime(time)
void watchdogTimerThread()
NSDictionary * propertyListRepresentation()
void addSampleWithTotalTime:selfTime:(OOTimeDelta totalTime, [selfTime] OOTimeDelta selfTime)
void setNativeTime:(double value)
void setTotalTime:(double value)
void setProfilerOverhead:(double value)
void setExtensionTime:(double value)
void setProfileEntries:(NSArray *value)