@@ -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,6 +1107,9 @@ 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
@@ -1140,6 +1157,8 @@ void EvalContextDestroy(EvalContext *ctx)
11401157 ctx -> remote_var_promises = NULL ;
11411158 }
11421159
1160+ SeqDestroy (ctx -> profiler .events );
1161+
11431162 free (ctx );
11441163 }
11451164}
@@ -1324,17 +1343,20 @@ static StackFrame *StackFrameNew(StackFrameType type, bool inherit_previous)
13241343 frame -> inherits_previous = inherit_previous ;
13251344 frame -> path = NULL ;
13261345 frame -> override_immutable = false;
1346+ frame -> start = EvalContextEventStart ();
1347+ frame -> event = NULL ;
13271348
13281349 return frame ;
13291350}
13301351
1331- static StackFrame * StackFrameNewBundle (const Bundle * owner , bool inherit_previous )
1352+ static StackFrame * StackFrameNewBundle (const Bundle * owner , bool inherit_previous , bool profiling )
13321353{
13331354 StackFrame * frame = StackFrameNew (STACK_FRAME_TYPE_BUNDLE , inherit_previous );
13341355
13351356 frame -> data .bundle .owner = owner ;
13361357 frame -> data .bundle .classes = ClassTableNew ();
13371358 frame -> data .bundle .vars = VariableTableNew ();
1359+ frame -> event = (profiling ) ? BundleToEventFrame (owner ) : NULL ;
13381360
13391361 return frame ;
13401362}
@@ -1358,11 +1380,12 @@ static StackFrame *StackFrameNewBundleSection(const BundleSection *owner)
13581380 return frame ;
13591381}
13601382
1361- static StackFrame * StackFrameNewPromise (const Promise * owner )
1383+ static StackFrame * StackFrameNewPromise (const Promise * owner , bool profiling )
13621384{
13631385 StackFrame * frame = StackFrameNew (STACK_FRAME_TYPE_PROMISE , true);
13641386
13651387 frame -> data .promise .owner = owner ;
1388+ frame -> event = (profiling ) ? PromiseToEventFrame (owner ) : NULL ;
13661389
13671390 return frame ;
13681391}
@@ -1411,9 +1434,10 @@ static void EvalContextStackPushFrame(EvalContext *ctx, StackFrame *frame)
14111434
14121435void EvalContextStackPushBundleFrame (EvalContext * ctx , const Bundle * owner , const Rlist * args , bool inherits_previous )
14131436{
1437+ assert (ctx != NULL );
14141438 assert (!LastStackFrame (ctx , 0 ) || LastStackFrame (ctx , 0 )-> type == STACK_FRAME_TYPE_PROMISE_ITERATION );
14151439
1416- EvalContextStackPushFrame (ctx , StackFrameNewBundle (owner , inherits_previous ));
1440+ EvalContextStackPushFrame (ctx , StackFrameNewBundle (owner , inherits_previous , ctx -> profiling ));
14171441
14181442 if (RlistLen (args ) > 0 )
14191443 {
@@ -1487,12 +1511,13 @@ void EvalContextStackPushBundleSectionFrame(EvalContext *ctx, const BundleSectio
14871511
14881512void EvalContextStackPushPromiseFrame (EvalContext * ctx , const Promise * owner )
14891513{
1514+ assert (ctx != NULL );
14901515 assert (LastStackFrame (ctx , 0 ));
14911516 assert (LastStackFrame (ctx , 0 )-> type == STACK_FRAME_TYPE_BUNDLE_SECTION );
14921517
14931518 EvalContextVariableClearMatch (ctx );
14941519
1495- StackFrame * frame = StackFrameNewPromise (owner );
1520+ StackFrame * frame = StackFrameNewPromise (owner , ctx -> profiling );
14961521
14971522 EvalContextStackPushFrame (ctx , frame );
14981523
@@ -1579,10 +1604,21 @@ Promise *EvalContextStackPushPromiseIterationFrame(EvalContext *ctx, const Promi
15791604
15801605void EvalContextStackPopFrame (EvalContext * ctx )
15811606{
1607+ assert (ctx != NULL );
15821608 assert (SeqLength (ctx -> stack ) > 0 );
15831609
15841610 StackFrame * last_frame = LastStackFrame (ctx , 0 );
15851611 StackFrameType last_frame_type = last_frame -> type ;
1612+ int64_t start = last_frame -> start ;
1613+ int64_t end = EvalContextEventStart ();
1614+
1615+ EventFrame * last_event = last_frame -> event ;
1616+ if (last_event != NULL )
1617+ {
1618+ assert (end >= start ); /* sanity check */
1619+ last_event -> elapsed = end - start ;
1620+ SeqAppend (ctx -> profiler .events , last_event );
1621+ }
15861622
15871623 switch (last_frame_type )
15881624 {
@@ -3847,3 +3883,157 @@ bool EvalContextOverrideImmutableGet(EvalContext *ctx)
38473883 assert (stack_frame != NULL );
38483884 return stack_frame -> override_immutable ;
38493885}
3886+
3887+ // ##############################################################
3888+
3889+ void EvalContextSetProfiling (EvalContext * ctx , bool profiling )
3890+ {
3891+ assert (ctx != NULL );
3892+ ctx -> profiling = profiling ;
3893+ }
3894+
3895+ static EventFrame * EventFrameCreate (char * type , const char * name , const char * source , SourceOffset offset )
3896+ {
3897+ EventFrame * event = (EventFrame * ) xmalloc (sizeof (EventFrame ));
3898+
3899+ event -> type = type ;
3900+ event -> name = SafeStringDuplicate (name );
3901+ event -> filename = GetAbsolutePath (source );
3902+ event -> elapsed = 0 ;
3903+ event -> offset = offset ;
3904+ event -> id = StringFormat ("%s_%s_%s_%ld_%ld" , type , name , source , offset .start , offset .line );
3905+
3906+ return event ;
3907+ }
3908+
3909+ static void EventFrameDestroy (EventFrame * event )
3910+ {
3911+ assert (event != NULL );
3912+ free (event -> filename );
3913+ free (event -> id );
3914+ free (event -> name );
3915+ free (event );
3916+ }
3917+
3918+ static JsonElement * EventToJson (EventFrame * event )
3919+ {
3920+ JsonElement * json_event = JsonObjectCreate (10 );
3921+
3922+ JsonObjectAppendString (json_event , "type" , event -> type );
3923+ JsonObjectAppendString (json_event , "name" , event -> name );
3924+ JsonObjectAppendString (json_event , "filename" , event -> filename );
3925+ JsonObjectAppendString (json_event , "id" , event -> id );
3926+ JsonObjectAppendInteger64 (json_event , "elapsed" , event -> elapsed );
3927+
3928+ JsonElement * offset = JsonObjectCreate (4 );
3929+ JsonObjectAppendInteger (offset , "start" , event -> offset .start );
3930+ JsonObjectAppendInteger (offset , "end" , event -> offset .end );
3931+ JsonObjectAppendInteger (offset , "context" , event -> offset .context );
3932+ JsonObjectAppendInteger (offset , "line" , event -> offset .line );
3933+
3934+ JsonObjectAppendObject (json_event , "offset" , offset );
3935+
3936+ return json_event ;
3937+ }
3938+
3939+ static EventFrame * BundleToEventFrame (const Bundle * bp )
3940+ {
3941+ assert (bp != NULL );
3942+ return EventFrameCreate ("bundle" , bp -> name , bp -> source_path , bp -> offset );
3943+ }
3944+
3945+ static EventFrame * PromiseToEventFrame (const Promise * pp )
3946+ {
3947+ assert (pp != NULL );
3948+ return EventFrameCreate ("promise" , PromiseGetPromiseType (pp ), PromiseGetBundle (pp )-> source_path , pp -> offset );
3949+ }
3950+
3951+ static EventFrame * FunctionToEventFrame (const FnCall * fp )
3952+ {
3953+ assert (fp != NULL );
3954+ return EventFrameCreate ("function" , fp -> name , PromiseGetBundle (fp -> caller )-> source_path , fp -> caller -> offset );
3955+ }
3956+
3957+ void EvalContextAddFunctionEvent (EvalContext * ctx , const FnCall * fp , int64_t start )
3958+ {
3959+ assert (ctx != NULL );
3960+ if (ctx -> profiling )
3961+ {
3962+ EventFrame * event = FunctionToEventFrame (fp );
3963+ int64_t end = EvalContextEventStart ();
3964+ assert (end >= start ); /* sanity check */
3965+ event -> elapsed = end - start ;
3966+ SeqAppend (ctx -> profiler .events , event );
3967+ }
3968+ }
3969+
3970+ int64_t EvalContextEventStart ()
3971+ {
3972+ struct timespec ts ;
3973+ clock_gettime (CLOCK_MONOTONIC , & ts );
3974+ return ts .tv_sec * 1000000000LL + ts .tv_nsec ;
3975+ }
3976+
3977+ void EvalContextProfilingStart (EvalContext * ctx )
3978+ {
3979+ ctx -> profiler .elapsed = EvalContextEventStart ();
3980+ }
3981+
3982+ static HashMap * SumEventFrames (Seq * events )
3983+ {
3984+ HashMap * map = HashMapNew ((MapHashFn ) StringHash , (MapKeyEqualFn ) StringEqual , NULL , NULL , 10 );
3985+
3986+ size_t length = SeqLength (events );
3987+ EventFrame * curr ;
3988+ EventFrame * prev ;
3989+ MapKeyValue * mkv ;
3990+ for (int i = 0 ; i < length ; i ++ )
3991+ {
3992+ curr = SeqAt (events , i );
3993+ mkv = HashMapGet (map , curr -> id );
3994+
3995+ if (mkv == NULL )
3996+ {
3997+ HashMapInsert (map , curr -> id , curr );
3998+ continue ;
3999+ }
4000+ prev = mkv -> value ;
4001+ prev -> elapsed += curr -> elapsed ;
4002+ }
4003+
4004+ return map ;
4005+ }
4006+
4007+ void EvalContextProfilingEnd (EvalContext * ctx , const Policy * policy )
4008+ {
4009+ int64_t end = EvalContextEventStart ();
4010+ int64_t start = ctx -> profiler .elapsed ;
4011+ assert (end >= start ); /* sanity check */
4012+
4013+ ctx -> profiler .elapsed = end - start ;
4014+
4015+ HashMap * map = SumEventFrames (ctx -> profiler .events );
4016+ JsonElement * profiling = JsonObjectCreate (2 );
4017+
4018+ JsonElement * json_policy = PolicyToJson (policy );
4019+ JsonObjectAppendObject (profiling , "policy" , json_policy );
4020+
4021+ JsonElement * events = JsonArrayCreate (10 );
4022+ {
4023+ HashMapIterator iter = HashMapIteratorInit (map );
4024+ MapKeyValue * mkv ;
4025+ while ((mkv = HashMapIteratorNext (& iter )) != NULL )
4026+ {
4027+ EventFrame * event = mkv -> value ;
4028+ JsonArrayAppendObject (events , EventToJson (event ));
4029+ }
4030+ }
4031+ JsonObjectAppendArray (profiling , "events" , events );
4032+
4033+ // write
4034+ Writer * writer = FileWriter (stdout );
4035+ JsonWrite (writer , profiling , 2 );
4036+ WriterClose (writer );
4037+
4038+ JsonDestroy (profiling );
4039+ }
0 commit comments