Skip to content

Commit 1c3e1ca

Browse files
committed
Implemented policy profiler in cf-agent
Changelog: The policy profiling logic is now implemented natively within the cf-agent, replacing the previous reliance on cf-agent logs and the cf-profile.pl script for faster execution. Ticket: ENT-8096 Signed-off-by: Victor Moene <[email protected]>
1 parent 42ebd9f commit 1c3e1ca

File tree

6 files changed

+270
-6
lines changed

6 files changed

+270
-6
lines changed

cf-agent/cf-agent.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ static const struct option OPTIONS[] =
209209
{"color", optional_argument, 0, 'C'},
210210
{"no-extensions", no_argument, 0, 'E'},
211211
{"timestamp", no_argument, 0, 'l'},
212+
{"profile", no_argument, 0, 'p'},
212213
/* Only long option for the rest */
213214
{"ignore-preferred-augments", no_argument, 0, 0},
214215
{"log-modules", required_argument, 0, 0},
@@ -245,6 +246,7 @@ static const char *const HINTS[] =
245246
"Enable colorized output. Possible values: 'always', 'auto', 'never'. If option is used, the default value is 'auto'",
246247
"Disable extension loading (used while upgrading)",
247248
"Log timestamps on each line of log output",
249+
"Output diagnostic of bundle execution",
248250
"Ignore def_preferred.json file in favor of def.json",
249251
"Enable even more detailed debug logging for specific areas of the implementation. Use together with '-d'. Use --log-modules=help for a list of available modules",
250252
"Do not load augments (def.json)",
@@ -323,7 +325,9 @@ int main(int argc, char *argv[])
323325

324326
BeginAudit();
325327

328+
int64_t policy_event_start = EvalContextEventStart();
326329
KeepPromises(ctx, policy, config);
330+
EvalContextWriteEventTree(ctx, policy_event_start);
327331

328332
if (EvalAborted(ctx))
329333
{
@@ -523,7 +527,7 @@ static GenericAgentConfig *CheckOpts(int argc, char **argv)
523527
FreeFixedStringArray(argc_new, argv_tmp);
524528

525529
int longopt_idx;
526-
while ((c = getopt_long(argc_new, argv_new, "tdvnKIf:g:w:D:N:VxMB:b:hC::ElT::",
530+
while ((c = getopt_long(argc_new, argv_new, "tdvnKIf:g:w:D:N:VxMB:b:hC::ElT::p",
527531
OPTIONS, &longopt_idx))
528532
!= -1)
529533
{
@@ -689,6 +693,9 @@ static GenericAgentConfig *CheckOpts(int argc, char **argv)
689693
}
690694

691695
break;
696+
case 'p':
697+
config->profiling = true;
698+
break;
692699

693700
/* long options only */
694701
case 0:

libpromises/eval_context.c

Lines changed: 233 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,14 @@ static bool EvalContextClassPut(EvalContext *ctx, const char *ns, const char *na
127127
static const char *EvalContextCurrentNamespace(const EvalContext *ctx);
128128
static 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

197211
void 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+
335362
static 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+
10611093
EvalContext *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

16281681
bool 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

Comments
 (0)