@@ -127,6 +127,14 @@ 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 , char * name , char * source , SourceOffset offset );
131+ static void EventFrameDestroy (EventFrame * event );
132+ static void EventFrameUpdate (EvalContext * ctx , EventFrame * last_event );
133+ static EventFrame * BundleToEventFrame (const Bundle * bp );
134+ static EventFrame * PromiseToEventFrame (const Promise * pp );
135+ static EventFrame * FunctionToEventFrame (const FnCall * fp );
136+ static EventFrame * PolicyToEventFrame ();
137+
130138/**
131139 * Every agent has only one EvalContext from process start to finish.
132140 */
@@ -192,6 +200,12 @@ struct EvalContext_
192200 RemoteVarPromisesMap * remote_var_promises ;
193201
194202 bool dump_reports ;
203+
204+ /* These are needed for policy profiling */
205+ bool profiling ;
206+ EventFrame * root ;
207+ HashMap * event_map ;
208+ Seq * events ;
195209};
196210
197211void EvalContextSetConfig (EvalContext * ctx , const GenericAgentConfig * config )
@@ -332,6 +346,19 @@ static StackFrame *LastStackFrameByType(const EvalContext *ctx, StackFrameType t
332346 return NULL ;
333347}
334348
349+ static StackFrame * LastStackFrameByNonNullEvent (const EvalContext * ctx )
350+ {
351+ for (size_t i = 0 ; i < SeqLength (ctx -> stack ); i ++ )
352+ {
353+ StackFrame * frame = LastStackFrame (ctx , i );
354+ if (frame -> event != NULL )
355+ {
356+ return frame ;
357+ }
358+ }
359+ return NULL ;
360+ }
361+
335362static LogLevel AdjustLogLevel (LogLevel base , LogLevel adjust )
336363{
337364 if (adjust == -1 )
@@ -1058,6 +1085,11 @@ ENTERPRISE_VOID_FUNC_1ARG_DEFINE_STUB(void, EvalContextSetupMissionPortalLogHook
10581085{
10591086}
10601087
1088+ void DestroyNothing (void * data )
1089+ {
1090+ assert (data != NULL );
1091+ }
1092+
10611093EvalContext * EvalContextNew (void )
10621094{
10631095 EvalContext * ctx = xcalloc (1 , sizeof (EvalContext ));
@@ -1093,6 +1125,10 @@ EvalContext *EvalContextNew(void)
10931125
10941126 ctx -> dump_reports = false;
10951127
1128+ ctx -> root = PolicyToEventFrame ();
1129+ ctx -> event_map = HashMapNew ((MapHashFn ) StringHash , (MapKeyEqualFn ) StringEqual , (MapDestroyDataFn ) DestroyNothing , (MapDestroyDataFn ) DestroyNothing , 10 );
1130+ ctx -> events = SeqNew (20 , EventFrameDestroy );
1131+
10961132 return ctx ;
10971133}
10981134
@@ -1140,6 +1176,10 @@ void EvalContextDestroy(EvalContext *ctx)
11401176 ctx -> remote_var_promises = NULL ;
11411177 }
11421178
1179+ HashMapDestroy (ctx -> event_map );
1180+ EventFrameDestroy (ctx -> root );
1181+ SeqDestroy (ctx -> events );
1182+
11431183 free (ctx );
11441184 }
11451185}
@@ -1324,17 +1364,20 @@ static StackFrame *StackFrameNew(StackFrameType type, bool inherit_previous)
13241364 frame -> inherits_previous = inherit_previous ;
13251365 frame -> path = NULL ;
13261366 frame -> override_immutable = false;
1367+ frame -> start = EvalContextEventStart ();
1368+ frame -> event = NULL ;
13271369
13281370 return frame ;
13291371}
13301372
1331- static StackFrame * StackFrameNewBundle (const Bundle * owner , bool inherit_previous )
1373+ static StackFrame * StackFrameNewBundle (const Bundle * owner , bool inherit_previous , bool profiling )
13321374{
13331375 StackFrame * frame = StackFrameNew (STACK_FRAME_TYPE_BUNDLE , inherit_previous );
13341376
13351377 frame -> data .bundle .owner = owner ;
13361378 frame -> data .bundle .classes = ClassTableNew ();
13371379 frame -> data .bundle .vars = VariableTableNew ();
1380+ frame -> event = (profiling ) ? BundleToEventFrame (owner ) : NULL ;
13381381
13391382 return frame ;
13401383}
@@ -1358,11 +1401,12 @@ static StackFrame *StackFrameNewBundleSection(const BundleSection *owner)
13581401 return frame ;
13591402}
13601403
1361- static StackFrame * StackFrameNewPromise (const Promise * owner )
1404+ static StackFrame * StackFrameNewPromise (const Promise * owner , bool profiling )
13621405{
13631406 StackFrame * frame = StackFrameNew (STACK_FRAME_TYPE_PROMISE , true);
13641407
13651408 frame -> data .promise .owner = owner ;
1409+ frame -> event = (profiling ) ? PromiseToEventFrame (owner ) : NULL ;
13661410
13671411 return frame ;
13681412}
@@ -1413,7 +1457,7 @@ void EvalContextStackPushBundleFrame(EvalContext *ctx, const Bundle *owner, cons
14131457{
14141458 assert (!LastStackFrame (ctx , 0 ) || LastStackFrame (ctx , 0 )-> type == STACK_FRAME_TYPE_PROMISE_ITERATION );
14151459
1416- EvalContextStackPushFrame (ctx , StackFrameNewBundle (owner , inherits_previous ));
1460+ EvalContextStackPushFrame (ctx , StackFrameNewBundle (owner , inherits_previous , ctx -> profiling ));
14171461
14181462 if (RlistLen (args ) > 0 )
14191463 {
@@ -1492,7 +1536,7 @@ void EvalContextStackPushPromiseFrame(EvalContext *ctx, const Promise *owner)
14921536
14931537 EvalContextVariableClearMatch (ctx );
14941538
1495- StackFrame * frame = StackFrameNewPromise (owner );
1539+ StackFrame * frame = StackFrameNewPromise (owner , ctx -> profiling );
14961540
14971541 EvalContextStackPushFrame (ctx , frame );
14981542
@@ -1583,6 +1627,9 @@ void EvalContextStackPopFrame(EvalContext *ctx)
15831627
15841628 StackFrame * last_frame = LastStackFrame (ctx , 0 );
15851629 StackFrameType last_frame_type = last_frame -> type ;
1630+ int64_t start = last_frame -> start ;
1631+ int64_t end = EvalContextEventStart ();
1632+ EventFrame * last_event = last_frame -> event ;
15861633
15871634 switch (last_frame_type )
15881635 {
@@ -1608,7 +1655,6 @@ void EvalContextStackPopFrame(EvalContext *ctx)
16081655 default :
16091656 break ;
16101657 }
1611-
16121658 SeqRemove (ctx -> stack , SeqLength (ctx -> stack ) - 1 );
16131659
16141660 last_frame = LastStackFrame (ctx , 0 );
@@ -1623,6 +1669,13 @@ void EvalContextStackPopFrame(EvalContext *ctx)
16231669
16241670 LogDebug (LOG_MOD_EVALCTX , "POPPED FRAME (type %s)" ,
16251671 STACK_FRAME_TYPE_STR [last_frame_type ]);
1672+
1673+ // if the last StackFrame has no event, we skip
1674+ if (last_event != NULL )
1675+ {
1676+ last_event -> elapsed = end - start ;
1677+ EventFrameUpdate (ctx , last_event );
1678+ }
16261679}
16271680
16281681bool EvalContextClassRemove (EvalContext * ctx , const char * ns , const char * name )
@@ -3847,3 +3900,178 @@ bool EvalContextOverrideImmutableGet(EvalContext *ctx)
38473900 assert (stack_frame != NULL );
38483901 return stack_frame -> override_immutable ;
38493902}
3903+
3904+ // ##############################################################
3905+
3906+ static EventFrame * EventFrameCreate (char * type , char * name , char * source , SourceOffset offset )
3907+ {
3908+ EventFrame * event = (EventFrame * ) xmalloc (sizeof (EventFrame ));
3909+
3910+ event -> type = type ;
3911+ event -> name = name ;
3912+ event -> filename = source ;
3913+ event -> elapsed = 0 ;
3914+ event -> children = SeqNew (10 , NULL );
3915+ event -> offset = offset ;
3916+ event -> parent = NULL ;
3917+
3918+ char id [strlen (type ) + strlen (name ) + strlen (source ) + PRINTSIZE (offset .start ) + PRINTSIZE (offset .line ) + 4 ];
3919+ xsnprintf (id , sizeof (id )+ 1 , "%s_%s_%s_%ld_%ld" , type , name , source , offset .start , offset .line );
3920+
3921+ event -> id = SafeStringDuplicate (id );
3922+
3923+ return event ;
3924+ }
3925+
3926+ static void EventFrameDestroy (EventFrame * event )
3927+ {
3928+ assert (event != NULL );
3929+ SeqDestroy (event -> children );
3930+ free (event -> filename );
3931+ free (event -> id );
3932+ free (event -> name );
3933+ free (event );
3934+ }
3935+
3936+ static int EventFrameCompare (const void * event1 , const void * event2 , void * user_data ARG_UNUSED )
3937+ {
3938+ const EventFrame * e1 = event1 ;
3939+ const EventFrame * e2 = event2 ;
3940+ return strcmp (e1 -> id , e2 -> id );
3941+ }
3942+
3943+ static void EventFrameUpdate (EvalContext * ctx , EventFrame * last_event )
3944+ {
3945+ assert (ctx != NULL );
3946+ assert (last_event != NULL );
3947+ SeqAppend (ctx -> events , last_event ); // keep track of EventFrames for cleanup
3948+
3949+ // if the event is already in the map and has shorter elapsed time, then we ignore it
3950+ // else, we replace the old with the latest one
3951+ MapKeyValue * mkv = HashMapGet (ctx -> event_map , last_event -> id );
3952+ if (mkv != NULL )
3953+ {
3954+ EventFrame * previous = mkv -> value ;
3955+
3956+ if (previous -> elapsed > last_event -> elapsed )
3957+ {
3958+ return ;
3959+ }
3960+ ssize_t i = SeqIndexOf (previous -> parent -> children , previous , EventFrameCompare );
3961+ SeqRemove (previous -> parent -> children , i );
3962+ }
3963+
3964+ // Insert the event in the map and attach the event to its parent
3965+ HashMapInsert (ctx -> event_map , last_event -> id , last_event );
3966+
3967+ StackFrame * parent_event_frame = LastStackFrameByNonNullEvent (ctx );
3968+ if (parent_event_frame != NULL )
3969+ {
3970+ SeqAppend (parent_event_frame -> event -> children , last_event );
3971+ last_event -> parent = parent_event_frame -> event ;
3972+ }
3973+ else
3974+ {
3975+ SeqAppend (ctx -> root -> children , last_event );
3976+ last_event -> parent = ctx -> root ;
3977+ }
3978+ }
3979+
3980+ static EventFrame * BundleToEventFrame (const Bundle * bp )
3981+ {
3982+ assert (bp != NULL );
3983+ return EventFrameCreate ("bundle" , SafeStringDuplicate (bp -> name ), GetAbsolutePath (bp -> source_path ), bp -> offset );
3984+ }
3985+
3986+ static EventFrame * PromiseToEventFrame (const Promise * pp )
3987+ {
3988+ assert (pp != NULL );
3989+ return EventFrameCreate ("promise" , SafeStringDuplicate (PromiseGetPromiseType (pp )), GetAbsolutePath (PromiseGetBundle (pp )-> source_path ), pp -> offset );
3990+ }
3991+
3992+ static EventFrame * FunctionToEventFrame (const FnCall * fp )
3993+ {
3994+ assert (fp != NULL );
3995+ return EventFrameCreate ("function" , SafeStringDuplicate (fp -> name ), GetAbsolutePath (PromiseGetBundle (fp -> caller )-> source_path ), fp -> caller -> offset );
3996+ }
3997+
3998+ static EventFrame * PolicyToEventFrame ()
3999+ {
4000+ return EventFrameCreate ("policy" , SafeStringDuplicate ("" ), SafeStringDuplicate ("" ), (SourceOffset ) {0 });
4001+ }
4002+
4003+ void EvalContextAddFunctionEvent (EvalContext * ctx , FnCall * fp , int64_t start )
4004+ {
4005+ assert (ctx != NULL );
4006+ if (ctx -> profiling )
4007+ {
4008+ EventFrame * event = FunctionToEventFrame (fp );
4009+ event -> elapsed = EvalContextEventStart () - start ;
4010+ EventFrameUpdate (ctx , event );
4011+ }
4012+ }
4013+
4014+ static JsonElement * EventFrameTreeToJson (const EventFrame * node )
4015+ {
4016+ if (node == NULL )
4017+ {
4018+ return NULL ;
4019+ }
4020+
4021+ JsonElement * new_element = JsonObjectCreate (6 );
4022+
4023+ JsonObjectAppendString (new_element , "name" , node -> name );
4024+ JsonObjectAppendString (new_element , "type" , node -> type );
4025+ JsonObjectAppendString (new_element , "filename" , node -> filename );
4026+ JsonObjectAppendInteger64 (new_element , "elapsed" , node -> elapsed );
4027+
4028+ JsonElement * offset = JsonObjectCreate (3 );
4029+ JsonObjectAppendInteger (offset , "lineno" , node -> offset .line );
4030+ JsonObjectAppendInteger (offset , "start" , node -> offset .start );
4031+ JsonObjectAppendInteger (offset , "end" , node -> offset .end );
4032+ JsonObjectAppendObject (new_element , "offset" , offset );
4033+
4034+ JsonElement * children = JsonArrayCreate (5 );
4035+ for (size_t i = 0 ; i < SeqLength (node -> children ); i ++ )
4036+ {
4037+ EventFrame * next = SeqAt (node -> children , i );
4038+ JsonElement * child = EventFrameTreeToJson (next );
4039+ if (child != NULL )
4040+ {
4041+ JsonArrayAppendObject (children , child );
4042+ }
4043+ }
4044+ JsonObjectAppendArray (new_element , "children" , children );
4045+ return new_element ;
4046+ }
4047+
4048+ int64_t EvalContextEventStart ()
4049+ {
4050+ struct timespec ts ;
4051+ clock_gettime (CLOCK_MONOTONIC , & ts );
4052+ return ts .tv_sec * 1000000000LL + ts .tv_nsec ;
4053+ }
4054+
4055+ void EvalContextWriteEventTree (EvalContext * ctx , int64_t start )
4056+ {
4057+ assert (ctx != NULL );
4058+ int64_t end = EvalContextEventStart ();
4059+ ctx -> root -> elapsed = end - start ;
4060+
4061+ JsonElement * tree = EventFrameTreeToJson (ctx -> root );
4062+
4063+ if (ctx -> profiling )
4064+ {
4065+ Writer * w = FileWriter (stdout );
4066+ JsonWrite (w , tree , 0 );
4067+ FileWriterDetach (w );
4068+ }
4069+
4070+ JsonDestroy (tree );
4071+ }
4072+
4073+ void EvalContextSetProfiling (EvalContext * ctx , bool profiling )
4074+ {
4075+ assert (ctx != NULL );
4076+ ctx -> profiling = profiling ;
4077+ }
0 commit comments