@@ -127,6 +127,12 @@ static bool EvalContextClassPut(EvalContext *ctx, const char *ns, const char *na
127127static const char * EvalContextCurrentNamespace (const EvalContext * ctx );
128128static ClassRef IDRefQualify (const EvalContext * ctx , const char * id );
129129
130+ static EventFrame * EventFrameCreate (char * type , const char * name , const char * source , SourceOffset offset );
131+ static void EventFrameDestroy (EventFrame * event );
132+ static EventFrame * BundleToEventFrame (const Bundle * bp );
133+ static EventFrame * PromiseToEventFrame (const Promise * pp );
134+ static EventFrame * FunctionToEventFrame (const FnCall * fp );
135+
130136/**
131137 * Every agent has only one EvalContext from process start to finish.
132138 */
@@ -192,6 +198,14 @@ struct EvalContext_
192198 RemoteVarPromisesMap * remote_var_promises ;
193199
194200 bool dump_reports ;
201+
202+ /* These are needed for policy profiling */
203+ bool profiling ;
204+
205+ struct {
206+ int64_t elapsed ;
207+ Seq * events ;
208+ } profiler ;
195209};
196210
197211void EvalContextSetConfig (EvalContext * ctx , const GenericAgentConfig * config )
@@ -1093,12 +1107,15 @@ EvalContext *EvalContextNew(void)
10931107
10941108 ctx -> dump_reports = false;
10951109
1110+ ctx -> profiler .events = SeqNew (20 , EventFrameDestroy );
1111+ ctx -> profiler .elapsed = 0 ;
1112+
10961113 return ctx ;
10971114}
10981115
10991116void EvalContextDestroy (EvalContext * ctx )
11001117{
1101- if (ctx )
1118+ if (ctx != NULL )
11021119 {
11031120 free (ctx -> launch_directory );
11041121 free (ctx -> entry_point );
@@ -1140,6 +1157,7 @@ void EvalContextDestroy(EvalContext *ctx)
11401157 ctx -> remote_var_promises = NULL ;
11411158 }
11421159
1160+ SeqDestroy (ctx -> profiler .events );
11431161 free (ctx );
11441162 }
11451163}
@@ -1324,17 +1342,20 @@ static StackFrame *StackFrameNew(StackFrameType type, bool inherit_previous)
13241342 frame -> inherits_previous = inherit_previous ;
13251343 frame -> path = NULL ;
13261344 frame -> override_immutable = false;
1345+ frame -> start = EvalContextEventStart ();
1346+ frame -> event = NULL ;
13271347
13281348 return frame ;
13291349}
13301350
1331- static StackFrame * StackFrameNewBundle (const Bundle * owner , bool inherit_previous )
1351+ static StackFrame * StackFrameNewBundle (const Bundle * owner , bool inherit_previous , bool profiling )
13321352{
13331353 StackFrame * frame = StackFrameNew (STACK_FRAME_TYPE_BUNDLE , inherit_previous );
13341354
13351355 frame -> data .bundle .owner = owner ;
13361356 frame -> data .bundle .classes = ClassTableNew ();
13371357 frame -> data .bundle .vars = VariableTableNew ();
1358+ frame -> event = (profiling ) ? BundleToEventFrame (owner ) : NULL ;
13381359
13391360 return frame ;
13401361}
@@ -1358,11 +1379,12 @@ static StackFrame *StackFrameNewBundleSection(const BundleSection *owner)
13581379 return frame ;
13591380}
13601381
1361- static StackFrame * StackFrameNewPromise (const Promise * owner )
1382+ static StackFrame * StackFrameNewPromise (const Promise * owner , bool profiling )
13621383{
13631384 StackFrame * frame = StackFrameNew (STACK_FRAME_TYPE_PROMISE , true);
13641385
13651386 frame -> data .promise .owner = owner ;
1387+ frame -> event = (profiling ) ? PromiseToEventFrame (owner ) : NULL ;
13661388
13671389 return frame ;
13681390}
@@ -1411,9 +1433,10 @@ static void EvalContextStackPushFrame(EvalContext *ctx, StackFrame *frame)
14111433
14121434void EvalContextStackPushBundleFrame (EvalContext * ctx , const Bundle * owner , const Rlist * args , bool inherits_previous )
14131435{
1436+ assert (ctx != NULL );
14141437 assert (!LastStackFrame (ctx , 0 ) || LastStackFrame (ctx , 0 )-> type == STACK_FRAME_TYPE_PROMISE_ITERATION );
14151438
1416- EvalContextStackPushFrame (ctx , StackFrameNewBundle (owner , inherits_previous ));
1439+ EvalContextStackPushFrame (ctx , StackFrameNewBundle (owner , inherits_previous , ctx -> profiling ));
14171440
14181441 if (RlistLen (args ) > 0 )
14191442 {
@@ -1487,12 +1510,13 @@ void EvalContextStackPushBundleSectionFrame(EvalContext *ctx, const BundleSectio
14871510
14881511void EvalContextStackPushPromiseFrame (EvalContext * ctx , const Promise * owner )
14891512{
1513+ assert (ctx != NULL );
14901514 assert (LastStackFrame (ctx , 0 ));
14911515 assert (LastStackFrame (ctx , 0 )-> type == STACK_FRAME_TYPE_BUNDLE_SECTION );
14921516
14931517 EvalContextVariableClearMatch (ctx );
14941518
1495- StackFrame * frame = StackFrameNewPromise (owner );
1519+ StackFrame * frame = StackFrameNewPromise (owner , ctx -> profiling );
14961520
14971521 EvalContextStackPushFrame (ctx , frame );
14981522
@@ -1579,10 +1603,21 @@ Promise *EvalContextStackPushPromiseIterationFrame(EvalContext *ctx, const Promi
15791603
15801604void EvalContextStackPopFrame (EvalContext * ctx )
15811605{
1606+ assert (ctx != NULL );
15821607 assert (SeqLength (ctx -> stack ) > 0 );
15831608
15841609 StackFrame * last_frame = LastStackFrame (ctx , 0 );
15851610 StackFrameType last_frame_type = last_frame -> type ;
1611+ int64_t start = last_frame -> start ;
1612+ int64_t end = EvalContextEventStart ();
1613+
1614+ EventFrame * last_event = last_frame -> event ;
1615+ if (last_event != NULL )
1616+ {
1617+ assert (end >= start ); /* sanity check */
1618+ last_event -> elapsed = end - start ;
1619+ SeqAppend (ctx -> profiler .events , last_event );
1620+ }
15861621
15871622 switch (last_frame_type )
15881623 {
@@ -3847,3 +3882,168 @@ bool EvalContextOverrideImmutableGet(EvalContext *ctx)
38473882 assert (stack_frame != NULL );
38483883 return stack_frame -> override_immutable ;
38493884}
3885+
3886+ // ##############################################################
3887+
3888+ void EvalContextSetProfiling (EvalContext * ctx , bool profiling )
3889+ {
3890+ assert (ctx != NULL );
3891+ ctx -> profiling = profiling ;
3892+ }
3893+
3894+ static EventFrame * EventFrameCreate (char * type , const char * name , const char * source , SourceOffset offset )
3895+ {
3896+ EventFrame * event = (EventFrame * ) xmalloc (sizeof (EventFrame ));
3897+
3898+ event -> type = type ;
3899+ event -> name = SafeStringDuplicate (name );
3900+ event -> filename = GetAbsolutePath (source );
3901+ event -> elapsed = 0 ;
3902+ event -> offset = offset ;
3903+ event -> id = StringFormat ("%s_%s_%s_%ld_%ld" , type , name , source , offset .start , offset .line );
3904+
3905+ return event ;
3906+ }
3907+
3908+ static void EventFrameDestroy (EventFrame * event )
3909+ {
3910+ if (event != NULL )
3911+ {
3912+ free (event -> filename );
3913+ free (event -> id );
3914+ free (event -> name );
3915+ free (event );
3916+ }
3917+ }
3918+
3919+ static JsonElement * EventToJson (EventFrame * event )
3920+ {
3921+ assert (event != NULL );
3922+ JsonElement * json_event = JsonObjectCreate (10 );
3923+
3924+ JsonObjectAppendString (json_event , "type" , event -> type );
3925+ JsonObjectAppendString (json_event , "name" , event -> name );
3926+ JsonObjectAppendString (json_event , "filename" , event -> filename );
3927+ JsonObjectAppendString (json_event , "id" , event -> id );
3928+ JsonObjectAppendInteger64 (json_event , "elapsed" , event -> elapsed );
3929+
3930+ JsonElement * offset = JsonObjectCreate (4 );
3931+ JsonObjectAppendInteger (offset , "start" , event -> offset .start );
3932+ JsonObjectAppendInteger (offset , "end" , event -> offset .end );
3933+ JsonObjectAppendInteger (offset , "context" , event -> offset .context );
3934+ JsonObjectAppendInteger (offset , "line" , event -> offset .line );
3935+
3936+ JsonObjectAppendObject (json_event , "offset" , offset );
3937+
3938+ return json_event ;
3939+ }
3940+
3941+ static EventFrame * BundleToEventFrame (const Bundle * bp )
3942+ {
3943+ assert (bp != NULL );
3944+ return EventFrameCreate ("bundle" , bp -> name , bp -> source_path , bp -> offset );
3945+ }
3946+
3947+ static EventFrame * PromiseToEventFrame (const Promise * pp )
3948+ {
3949+ assert (pp != NULL );
3950+ return EventFrameCreate ("promise" , PromiseGetPromiseType (pp ), PromiseGetBundle (pp )-> source_path , pp -> offset );
3951+ }
3952+
3953+ static EventFrame * FunctionToEventFrame (const FnCall * fp )
3954+ {
3955+ assert (fp != NULL );
3956+ return EventFrameCreate ("function" , fp -> name , PromiseGetBundle (fp -> caller )-> source_path , fp -> caller -> offset );
3957+ }
3958+
3959+ void EvalContextAddFunctionEvent (EvalContext * ctx , const FnCall * fp , int64_t start )
3960+ {
3961+ assert (ctx != NULL );
3962+ if (ctx -> profiling )
3963+ {
3964+ EventFrame * event = FunctionToEventFrame (fp );
3965+ int64_t end = EvalContextEventStart ();
3966+ assert (end >= start ); /* sanity check */
3967+ event -> elapsed = end - start ;
3968+ SeqAppend (ctx -> profiler .events , event );
3969+ }
3970+ }
3971+
3972+ int64_t EvalContextEventStart ()
3973+ {
3974+ struct timespec ts ;
3975+ clock_gettime (CLOCK_MONOTONIC , & ts );
3976+ return ts .tv_sec * 1000000000LL + ts .tv_nsec ;
3977+ }
3978+
3979+ void EvalContextProfilingStart (EvalContext * ctx )
3980+ {
3981+ assert (ctx != NULL );
3982+ ctx -> profiler .elapsed = EvalContextEventStart ();
3983+ }
3984+
3985+ static HashMap * SumEventFrames (Seq * events )
3986+ {
3987+ HashMap * map = HashMapNew ((MapHashFn ) StringHash , (MapKeyEqualFn ) StringEqual , NULL , NULL , 10 );
3988+
3989+ size_t length = SeqLength (events );
3990+ EventFrame * curr ;
3991+ EventFrame * prev ;
3992+ MapKeyValue * mkv ;
3993+ for (int i = 0 ; i < length ; i ++ )
3994+ {
3995+ curr = SeqAt (events , i );
3996+ mkv = HashMapGet (map , curr -> id );
3997+
3998+ if (mkv == NULL )
3999+ {
4000+ HashMapInsert (map , curr -> id , curr );
4001+ continue ;
4002+ }
4003+ prev = mkv -> value ;
4004+ prev -> elapsed += curr -> elapsed ;
4005+ }
4006+
4007+ return map ;
4008+ }
4009+
4010+ void EvalContextProfilingEnd (EvalContext * ctx , const Policy * policy )
4011+ {
4012+ assert (ctx != NULL );
4013+
4014+ if (!ctx -> profiling )
4015+ {
4016+ return ;
4017+ }
4018+
4019+ int64_t end = EvalContextEventStart ();
4020+ int64_t start = ctx -> profiler .elapsed ;
4021+ assert (end >= start ); /* sanity check */
4022+
4023+ ctx -> profiler .elapsed = end - start ;
4024+
4025+ HashMap * map = SumEventFrames (ctx -> profiler .events );
4026+ JsonElement * profiling = JsonObjectCreate (2 );
4027+
4028+ JsonElement * json_policy = PolicyToJson (policy );
4029+ JsonObjectAppendObject (profiling , "policy" , json_policy );
4030+
4031+ JsonElement * events = JsonArrayCreate (10 );
4032+ {
4033+ HashMapIterator iter = HashMapIteratorInit (map );
4034+ MapKeyValue * mkv ;
4035+ while ((mkv = HashMapIteratorNext (& iter )) != NULL )
4036+ {
4037+ EventFrame * event = mkv -> value ;
4038+ JsonArrayAppendObject (events , EventToJson (event ));
4039+ }
4040+ }
4041+ JsonObjectAppendArray (profiling , "events" , events );
4042+
4043+ // write
4044+ Writer * writer = FileWriter (stdout );
4045+ JsonWrite (writer , profiling , 2 );
4046+ WriterClose (writer );
4047+
4048+ JsonDestroy (profiling );
4049+ }
0 commit comments