Skip to content

Commit 6347574

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 6347574

File tree

6 files changed

+271
-6
lines changed

6 files changed

+271
-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: 234 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,15 @@ 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 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

197212
void 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+
335363
static 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+
10611094
EvalContext *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

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

Comments
 (0)