@@ -127,6 +127,15 @@ 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 int EventFrameCompare (EventFrame * a , EventFrame * b );
133+ static void EventFrameUpdate (EvalContext * ctx , EventFrame * last_event );
134+ static EventFrame * BundleToEventFrame (const Bundle * bp );
135+ static EventFrame * PromiseToEventFrame (const Promise * pp );
136+ static EventFrame * FunctionToEventFrame (const FnCall * fp );
137+ static EventFrame * PolicyToEventFrame ();
138+
130139/**
131140 * Every agent has only one EvalContext from process start to finish.
132141 */
@@ -192,6 +201,12 @@ struct EvalContext_
192201 RemoteVarPromisesMap * remote_var_promises ;
193202
194203 bool dump_reports ;
204+
205+ /* These are needed for policy profiling */
206+ bool profiling ;
207+ EventFrame * root ;
208+ HashMap * event_map ;
209+ Seq * events ;
195210};
196211
197212void EvalContextSetConfig (EvalContext * ctx , const GenericAgentConfig * config )
@@ -332,6 +347,19 @@ static StackFrame *LastStackFrameByType(const EvalContext *ctx, StackFrameType t
332347 return NULL ;
333348}
334349
350+ static StackFrame * LastStackFrameByNonNullEvent (const EvalContext * ctx )
351+ {
352+ for (size_t i = 0 ; i < SeqLength (ctx -> stack ); i ++ )
353+ {
354+ StackFrame * frame = LastStackFrame (ctx , i );
355+ if (frame -> event != NULL )
356+ {
357+ return frame ;
358+ }
359+ }
360+ return NULL ;
361+ }
362+
335363static LogLevel AdjustLogLevel (LogLevel base , LogLevel adjust )
336364{
337365 if (adjust == -1 )
@@ -1058,6 +1086,11 @@ ENTERPRISE_VOID_FUNC_1ARG_DEFINE_STUB(void, EvalContextSetupMissionPortalLogHook
10581086{
10591087}
10601088
1089+ void DestroyNothing (void * data )
1090+ {
1091+ assert (data != NULL );
1092+ }
1093+
10611094EvalContext * EvalContextNew (void )
10621095{
10631096 EvalContext * ctx = xcalloc (1 , sizeof (EvalContext ));
@@ -1093,6 +1126,10 @@ EvalContext *EvalContextNew(void)
10931126
10941127 ctx -> dump_reports = false;
10951128
1129+ ctx -> root = PolicyToEventFrame ();
1130+ ctx -> event_map = HashMapNew ((MapHashFn ) StringHash , (MapKeyEqualFn ) StringEqual , (MapDestroyDataFn ) DestroyNothing , (MapDestroyDataFn ) DestroyNothing , 10 );
1131+ ctx -> events = SeqNew (20 , EventFrameDestroy );
1132+
10961133 return ctx ;
10971134}
10981135
@@ -1140,6 +1177,10 @@ void EvalContextDestroy(EvalContext *ctx)
11401177 ctx -> remote_var_promises = NULL ;
11411178 }
11421179
1180+ HashMapDestroy (ctx -> event_map );
1181+ EventFrameDestroy (ctx -> root );
1182+ SeqDestroy (ctx -> events );
1183+
11431184 free (ctx );
11441185 }
11451186}
@@ -1324,17 +1365,20 @@ static StackFrame *StackFrameNew(StackFrameType type, bool inherit_previous)
13241365 frame -> inherits_previous = inherit_previous ;
13251366 frame -> path = NULL ;
13261367 frame -> override_immutable = false;
1368+ frame -> start = EvalContextEventStart ();
1369+ frame -> event = NULL ;
13271370
13281371 return frame ;
13291372}
13301373
1331- static StackFrame * StackFrameNewBundle (const Bundle * owner , bool inherit_previous )
1374+ static StackFrame * StackFrameNewBundle (const Bundle * owner , bool inherit_previous , bool profiling )
13321375{
13331376 StackFrame * frame = StackFrameNew (STACK_FRAME_TYPE_BUNDLE , inherit_previous );
13341377
13351378 frame -> data .bundle .owner = owner ;
13361379 frame -> data .bundle .classes = ClassTableNew ();
13371380 frame -> data .bundle .vars = VariableTableNew ();
1381+ frame -> event = (profiling ) ? BundleToEventFrame (owner ) : NULL ;
13381382
13391383 return frame ;
13401384}
@@ -1358,11 +1402,12 @@ static StackFrame *StackFrameNewBundleSection(const BundleSection *owner)
13581402 return frame ;
13591403}
13601404
1361- static StackFrame * StackFrameNewPromise (const Promise * owner )
1405+ static StackFrame * StackFrameNewPromise (const Promise * owner , bool profiling )
13621406{
13631407 StackFrame * frame = StackFrameNew (STACK_FRAME_TYPE_PROMISE , true);
13641408
13651409 frame -> data .promise .owner = owner ;
1410+ frame -> event = (profiling ) ? PromiseToEventFrame (owner ) : NULL ;
13661411
13671412 return frame ;
13681413}
@@ -1413,7 +1458,7 @@ void EvalContextStackPushBundleFrame(EvalContext *ctx, const Bundle *owner, cons
14131458{
14141459 assert (!LastStackFrame (ctx , 0 ) || LastStackFrame (ctx , 0 )-> type == STACK_FRAME_TYPE_PROMISE_ITERATION );
14151460
1416- EvalContextStackPushFrame (ctx , StackFrameNewBundle (owner , inherits_previous ));
1461+ EvalContextStackPushFrame (ctx , StackFrameNewBundle (owner , inherits_previous , ctx -> profiling ));
14171462
14181463 if (RlistLen (args ) > 0 )
14191464 {
@@ -1492,7 +1537,7 @@ void EvalContextStackPushPromiseFrame(EvalContext *ctx, const Promise *owner)
14921537
14931538 EvalContextVariableClearMatch (ctx );
14941539
1495- StackFrame * frame = StackFrameNewPromise (owner );
1540+ StackFrame * frame = StackFrameNewPromise (owner , ctx -> profiling );
14961541
14971542 EvalContextStackPushFrame (ctx , frame );
14981543
@@ -1583,6 +1628,9 @@ void EvalContextStackPopFrame(EvalContext *ctx)
15831628
15841629 StackFrame * last_frame = LastStackFrame (ctx , 0 );
15851630 StackFrameType last_frame_type = last_frame -> type ;
1631+ int64_t start = last_frame -> start ;
1632+ int64_t end = EvalContextEventStart ();
1633+ EventFrame * last_event = last_frame -> event ;
15861634
15871635 switch (last_frame_type )
15881636 {
@@ -1608,7 +1656,6 @@ void EvalContextStackPopFrame(EvalContext *ctx)
16081656 default :
16091657 break ;
16101658 }
1611-
16121659 SeqRemove (ctx -> stack , SeqLength (ctx -> stack ) - 1 );
16131660
16141661 last_frame = LastStackFrame (ctx , 0 );
@@ -1623,6 +1670,13 @@ void EvalContextStackPopFrame(EvalContext *ctx)
16231670
16241671 LogDebug (LOG_MOD_EVALCTX , "POPPED FRAME (type %s)" ,
16251672 STACK_FRAME_TYPE_STR [last_frame_type ]);
1673+
1674+ // if the last StackFrame has no event, we skip
1675+ if (last_event != NULL )
1676+ {
1677+ last_event -> elapsed = end - start ;
1678+ EventFrameUpdate (ctx , last_event );
1679+ }
16261680}
16271681
16281682bool EvalContextClassRemove (EvalContext * ctx , const char * ns , const char * name )
@@ -3847,3 +3901,178 @@ bool EvalContextOverrideImmutableGet(EvalContext *ctx)
38473901 assert (stack_frame != NULL );
38483902 return stack_frame -> override_immutable ;
38493903}
3904+
3905+ // ##############################################################
3906+
3907+ static EventFrame * EventFrameCreate (char * type , char * name , char * source , SourceOffset offset )
3908+ {
3909+ EventFrame * event = (EventFrame * ) xmalloc (sizeof (EventFrame ));
3910+
3911+ event -> type = type ;
3912+ event -> name = name ;
3913+ event -> filename = source ;
3914+ event -> elapsed = 0 ;
3915+ event -> children = SeqNew (10 , NULL );
3916+ event -> offset = offset ;
3917+ event -> parent = NULL ;
3918+
3919+ char id [strlen (type ) + strlen (name ) + strlen (source ) + PRINTSIZE (offset .start ) + PRINTSIZE (offset .line ) + 4 ];
3920+ xsnprintf (id , sizeof (id )+ 1 , "%s_%s_%s_%ld_%ld" , type , name , source , offset .start , offset .line );
3921+
3922+ event -> id = SafeStringDuplicate (id );
3923+
3924+ return event ;
3925+ }
3926+
3927+ static void EventFrameDestroy (EventFrame * event )
3928+ {
3929+ assert (event != NULL );
3930+ SeqDestroy (event -> children );
3931+ free (event -> filename );
3932+ free (event -> id );
3933+ free (event -> name );
3934+ free (event );
3935+ }
3936+
3937+ static int EventFrameCompare (EventFrame * a , EventFrame * b )
3938+ {
3939+ assert (a != NULL );
3940+ assert (b != NULL );
3941+ return strcmp (a -> id , b -> id );
3942+ }
3943+
3944+ static void EventFrameUpdate (EvalContext * ctx , EventFrame * last_event )
3945+ {
3946+ assert (ctx != NULL );
3947+ assert (last_event != NULL );
3948+ SeqAppend (ctx -> events , last_event ); // keep track of EventFrames for cleanup
3949+
3950+ // if the event is already in the map and has shorter elapsed time, then we ignore it
3951+ // else, we replace the old with the latest one
3952+ MapKeyValue * mkv = HashMapGet (ctx -> event_map , last_event -> id );
3953+ if (mkv != NULL )
3954+ {
3955+ EventFrame * previous = mkv -> value ;
3956+
3957+ if (previous -> elapsed > last_event -> elapsed )
3958+ {
3959+ return ;
3960+ }
3961+ ssize_t i = SeqIndexOf (previous -> parent -> children , previous , (SeqItemComparator ) EventFrameCompare );
3962+ SeqRemove (previous -> parent -> children , i );
3963+ }
3964+
3965+ // Insert the event in the map and attach the event to its parent
3966+ HashMapInsert (ctx -> event_map , last_event -> id , last_event );
3967+
3968+ StackFrame * parent_event_frame = LastStackFrameByNonNullEvent (ctx );
3969+ if (parent_event_frame != NULL )
3970+ {
3971+ SeqAppend (parent_event_frame -> event -> children , last_event );
3972+ last_event -> parent = parent_event_frame -> event ;
3973+ }
3974+ else
3975+ {
3976+ SeqAppend (ctx -> root -> children , last_event );
3977+ last_event -> parent = ctx -> root ;
3978+ }
3979+ }
3980+
3981+ static EventFrame * BundleToEventFrame (const Bundle * bp )
3982+ {
3983+ assert (bp != NULL );
3984+ return EventFrameCreate ("bundle" , SafeStringDuplicate (bp -> name ), GetAbsolutePath (bp -> source_path ), bp -> offset );
3985+ }
3986+
3987+ static EventFrame * PromiseToEventFrame (const Promise * pp )
3988+ {
3989+ assert (pp != NULL );
3990+ return EventFrameCreate ("promise" , SafeStringDuplicate (PromiseGetPromiseType (pp )), GetAbsolutePath (PromiseGetBundle (pp )-> source_path ), pp -> offset );
3991+ }
3992+
3993+ static EventFrame * FunctionToEventFrame (const FnCall * fp )
3994+ {
3995+ assert (fp != NULL );
3996+ return EventFrameCreate ("function" , SafeStringDuplicate (fp -> name ), GetAbsolutePath (PromiseGetBundle (fp -> caller )-> source_path ), fp -> caller -> offset );
3997+ }
3998+
3999+ static EventFrame * PolicyToEventFrame ()
4000+ {
4001+ return EventFrameCreate ("policy" , SafeStringDuplicate ("" ), SafeStringDuplicate ("" ), (SourceOffset ) {0 });
4002+ }
4003+
4004+ void EvalContextAddFunctionEvent (EvalContext * ctx , FnCall * fp , int64_t start )
4005+ {
4006+ assert (ctx != NULL );
4007+ if (ctx -> profiling )
4008+ {
4009+ EventFrame * event = FunctionToEventFrame (fp );
4010+ event -> elapsed = EvalContextEventStart () - start ;
4011+ EventFrameUpdate (ctx , event );
4012+ }
4013+ }
4014+
4015+ static JsonElement * EventFrameTreeToJson (const EventFrame * node )
4016+ {
4017+ if (node == NULL )
4018+ {
4019+ return NULL ;
4020+ }
4021+
4022+ JsonElement * new_element = JsonObjectCreate (6 );
4023+
4024+ JsonObjectAppendString (new_element , "name" , node -> name );
4025+ JsonObjectAppendString (new_element , "type" , node -> type );
4026+ JsonObjectAppendString (new_element , "filename" , node -> filename );
4027+ JsonObjectAppendInteger64 (new_element , "elapsed" , node -> elapsed );
4028+
4029+ JsonElement * offset = JsonObjectCreate (3 );
4030+ JsonObjectAppendInteger (offset , "lineno" , node -> offset .line );
4031+ JsonObjectAppendInteger (offset , "start" , node -> offset .start );
4032+ JsonObjectAppendInteger (offset , "end" , node -> offset .end );
4033+ JsonObjectAppendObject (new_element , "offset" , offset );
4034+
4035+ JsonElement * children = JsonArrayCreate (5 );
4036+ for (size_t i = 0 ; i < SeqLength (node -> children ); i ++ )
4037+ {
4038+ EventFrame * next = SeqAt (node -> children , i );
4039+ JsonElement * child = EventFrameTreeToJson (next );
4040+ if (child != NULL )
4041+ {
4042+ JsonArrayAppendObject (children , child );
4043+ }
4044+ }
4045+ JsonObjectAppendArray (new_element , "children" , children );
4046+ return new_element ;
4047+ }
4048+
4049+ int64_t EvalContextEventStart ()
4050+ {
4051+ struct timespec ts ;
4052+ clock_gettime (CLOCK_MONOTONIC , & ts );
4053+ return ts .tv_sec * 1000000000LL + ts .tv_nsec ;
4054+ }
4055+
4056+ void EvalContextWriteEventTree (EvalContext * ctx , int64_t start )
4057+ {
4058+ assert (ctx != NULL );
4059+ int64_t end = EvalContextEventStart ();
4060+ ctx -> root -> elapsed = end - start ;
4061+
4062+ JsonElement * tree = EventFrameTreeToJson (ctx -> root );
4063+
4064+ if (ctx -> profiling )
4065+ {
4066+ Writer * w = FileWriter (stdout );
4067+ JsonWrite (w , tree , 0 );
4068+ FileWriterDetach (w );
4069+ }
4070+
4071+ JsonDestroy (tree );
4072+ }
4073+
4074+ void EvalContextSetProfiling (EvalContext * ctx , bool profiling )
4075+ {
4076+ assert (ctx != NULL );
4077+ ctx -> profiling = profiling ;
4078+ }
0 commit comments