Skip to content

Commit 099fa71

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 <victor.moene@northern.tech>
1 parent 42ebd9f commit 099fa71

File tree

6 files changed

+229
-5
lines changed

6 files changed

+229
-5
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+
EvalContextProfilingStart(ctx);
326329
KeepPromises(ctx, policy, config);
330+
EvalContextProfilingEnd(ctx, policy);
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: 194 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ 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, 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

197211
void 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

14121435
void 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

14881512
void 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

15801605
void 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+
}

libpromises/eval_context.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,15 @@
4040
#include <ring_buffer.h>
4141
#include <generic_agent.h>
4242

43+
typedef struct {
44+
char *id;
45+
char *type;
46+
char *name;
47+
char *filename;
48+
SourceOffset offset;
49+
int64_t elapsed;
50+
} EventFrame;
51+
4352
typedef enum
4453
{
4554
STACK_FRAME_TYPE_BUNDLE,
@@ -101,6 +110,8 @@ typedef struct
101110

102111
char *path;
103112
bool override_immutable;
113+
int64_t start;
114+
EventFrame *event;
104115
} StackFrame;
105116

106117
typedef enum
@@ -434,4 +445,12 @@ void EvalContextSetDumpReports(EvalContext *ctx, bool dump_reports);
434445
bool EvalContextGetDumpReports(EvalContext *ctx);
435446
void EvalContextUpdateDumpReports(EvalContext *ctx);
436447

448+
int64_t EvalContextEventStart();
449+
void EvalContextAddFunctionEvent(EvalContext *ctx, const FnCall *fp, int64_t start);
450+
451+
void EvalContextSetProfiling(EvalContext *ctx, bool profiling);
452+
453+
void EvalContextProfilingStart(EvalContext *ctx);
454+
void EvalContextProfilingEnd(EvalContext *ctx, const Policy *policy);
455+
437456
#endif

libpromises/fncall.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,9 @@ FnCallResult FnCallEvaluate(EvalContext *ctx, const Policy *policy, FnCall *fp,
419419
WriterClose(fncall_writer);
420420
}
421421

422+
int64_t start = EvalContextEventStart();
422423
FnCallResult result = CallFunction(ctx, policy, fp, expargs);
424+
EvalContextAddFunctionEvent(ctx, fp, start);
423425

424426
if (result.status == FNCALL_FAILURE)
425427
{

0 commit comments

Comments
 (0)