@@ -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