Skip to content

Commit c3b36c1

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 c3b36c1

File tree

6 files changed

+240
-6
lines changed

6 files changed

+240
-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 (experimental)",
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: 205 additions & 5 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,12 +1107,15 @@ 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

10991116
void EvalContextDestroy(EvalContext *ctx)
11001117
{
1101-
if (ctx)
1118+
if (ctx != NULL)
11021119
{
11031120
free(ctx->launch_directory);
11041121
free(ctx->entry_point);
@@ -1140,6 +1157,7 @@ void EvalContextDestroy(EvalContext *ctx)
11401157
ctx->remote_var_promises = NULL;
11411158
}
11421159

1160+
SeqDestroy(ctx->profiler.events);
11431161
free(ctx);
11441162
}
11451163
}
@@ -1324,17 +1342,20 @@ static StackFrame *StackFrameNew(StackFrameType type, bool inherit_previous)
13241342
frame->inherits_previous = inherit_previous;
13251343
frame->path = NULL;
13261344
frame->override_immutable = false;
1345+
frame->start = EvalContextEventStart();
1346+
frame->event = NULL;
13271347

13281348
return frame;
13291349
}
13301350

1331-
static StackFrame *StackFrameNewBundle(const Bundle *owner, bool inherit_previous)
1351+
static StackFrame *StackFrameNewBundle(const Bundle *owner, bool inherit_previous, bool profiling)
13321352
{
13331353
StackFrame *frame = StackFrameNew(STACK_FRAME_TYPE_BUNDLE, inherit_previous);
13341354

13351355
frame->data.bundle.owner = owner;
13361356
frame->data.bundle.classes = ClassTableNew();
13371357
frame->data.bundle.vars = VariableTableNew();
1358+
frame->event = (profiling) ? BundleToEventFrame(owner) : NULL;
13381359

13391360
return frame;
13401361
}
@@ -1358,11 +1379,12 @@ static StackFrame *StackFrameNewBundleSection(const BundleSection *owner)
13581379
return frame;
13591380
}
13601381

1361-
static StackFrame *StackFrameNewPromise(const Promise *owner)
1382+
static StackFrame *StackFrameNewPromise(const Promise *owner, bool profiling)
13621383
{
13631384
StackFrame *frame = StackFrameNew(STACK_FRAME_TYPE_PROMISE, true);
13641385

13651386
frame->data.promise.owner = owner;
1387+
frame->event = (profiling) ? PromiseToEventFrame(owner) : NULL;
13661388

13671389
return frame;
13681390
}
@@ -1411,9 +1433,10 @@ static void EvalContextStackPushFrame(EvalContext *ctx, StackFrame *frame)
14111433

14121434
void EvalContextStackPushBundleFrame(EvalContext *ctx, const Bundle *owner, const Rlist *args, bool inherits_previous)
14131435
{
1436+
assert(ctx != NULL);
14141437
assert(!LastStackFrame(ctx, 0) || LastStackFrame(ctx, 0)->type == STACK_FRAME_TYPE_PROMISE_ITERATION);
14151438

1416-
EvalContextStackPushFrame(ctx, StackFrameNewBundle(owner, inherits_previous));
1439+
EvalContextStackPushFrame(ctx, StackFrameNewBundle(owner, inherits_previous, ctx->profiling));
14171440

14181441
if (RlistLen(args) > 0)
14191442
{
@@ -1487,12 +1510,13 @@ void EvalContextStackPushBundleSectionFrame(EvalContext *ctx, const BundleSectio
14871510

14881511
void EvalContextStackPushPromiseFrame(EvalContext *ctx, const Promise *owner)
14891512
{
1513+
assert(ctx != NULL);
14901514
assert(LastStackFrame(ctx, 0));
14911515
assert(LastStackFrame(ctx, 0)->type == STACK_FRAME_TYPE_BUNDLE_SECTION);
14921516

14931517
EvalContextVariableClearMatch(ctx);
14941518

1495-
StackFrame *frame = StackFrameNewPromise(owner);
1519+
StackFrame *frame = StackFrameNewPromise(owner, ctx->profiling);
14961520

14971521
EvalContextStackPushFrame(ctx, frame);
14981522

@@ -1579,10 +1603,21 @@ Promise *EvalContextStackPushPromiseIterationFrame(EvalContext *ctx, const Promi
15791603

15801604
void EvalContextStackPopFrame(EvalContext *ctx)
15811605
{
1606+
assert(ctx != NULL);
15821607
assert(SeqLength(ctx->stack) > 0);
15831608

15841609
StackFrame *last_frame = LastStackFrame(ctx, 0);
15851610
StackFrameType last_frame_type = last_frame->type;
1611+
int64_t start = last_frame->start;
1612+
int64_t end = EvalContextEventStart();
1613+
1614+
EventFrame *last_event = last_frame->event;
1615+
if (last_event != NULL)
1616+
{
1617+
assert(end >= start); /* sanity check */
1618+
last_event->elapsed = end - start;
1619+
SeqAppend(ctx->profiler.events, last_event);
1620+
}
15861621

15871622
switch (last_frame_type)
15881623
{
@@ -3847,3 +3882,168 @@ bool EvalContextOverrideImmutableGet(EvalContext *ctx)
38473882
assert(stack_frame != NULL);
38483883
return stack_frame->override_immutable;
38493884
}
3885+
3886+
// ##############################################################
3887+
3888+
void EvalContextSetProfiling(EvalContext *ctx, bool profiling)
3889+
{
3890+
assert(ctx != NULL);
3891+
ctx->profiling = profiling;
3892+
}
3893+
3894+
static EventFrame *EventFrameCreate(char *type, const char *name, const char *source, SourceOffset offset)
3895+
{
3896+
EventFrame *event = (EventFrame *) xmalloc(sizeof(EventFrame));
3897+
3898+
event->type = type;
3899+
event->name = SafeStringDuplicate(name);
3900+
event->filename = GetAbsolutePath(source);
3901+
event->elapsed = 0;
3902+
event->offset = offset;
3903+
event->id = StringFormat("%s_%s_%s_%ld_%ld", type, name, source, offset.start, offset.line);
3904+
3905+
return event;
3906+
}
3907+
3908+
static void EventFrameDestroy(EventFrame *event)
3909+
{
3910+
if (event != NULL)
3911+
{
3912+
free(event->filename);
3913+
free(event->id);
3914+
free(event->name);
3915+
free(event);
3916+
}
3917+
}
3918+
3919+
static JsonElement *EventToJson(EventFrame *event)
3920+
{
3921+
assert(event != NULL);
3922+
JsonElement *json_event = JsonObjectCreate(10);
3923+
3924+
JsonObjectAppendString(json_event, "type", event->type);
3925+
JsonObjectAppendString(json_event, "name", event->name);
3926+
JsonObjectAppendString(json_event, "filename", event->filename);
3927+
JsonObjectAppendString(json_event, "id", event->id);
3928+
JsonObjectAppendInteger64(json_event, "elapsed", event->elapsed);
3929+
3930+
JsonElement *offset = JsonObjectCreate(4);
3931+
JsonObjectAppendInteger(offset, "start", event->offset.start);
3932+
JsonObjectAppendInteger(offset, "end", event->offset.end);
3933+
JsonObjectAppendInteger(offset, "context", event->offset.context);
3934+
JsonObjectAppendInteger(offset, "line", event->offset.line);
3935+
3936+
JsonObjectAppendObject(json_event, "offset", offset);
3937+
3938+
return json_event;
3939+
}
3940+
3941+
static EventFrame *BundleToEventFrame(const Bundle *bp)
3942+
{
3943+
assert(bp != NULL);
3944+
return EventFrameCreate("bundle", bp->name, bp->source_path, bp->offset);
3945+
}
3946+
3947+
static EventFrame *PromiseToEventFrame(const Promise *pp)
3948+
{
3949+
assert(pp != NULL);
3950+
return EventFrameCreate("promise", PromiseGetPromiseType(pp), PromiseGetBundle(pp)->source_path, pp->offset);
3951+
}
3952+
3953+
static EventFrame *FunctionToEventFrame(const FnCall *fp)
3954+
{
3955+
assert(fp != NULL);
3956+
return EventFrameCreate("function", fp->name, PromiseGetBundle(fp->caller)->source_path, fp->caller->offset);
3957+
}
3958+
3959+
void EvalContextAddFunctionEvent(EvalContext *ctx, const FnCall *fp, int64_t start)
3960+
{
3961+
assert(ctx != NULL);
3962+
if (ctx->profiling)
3963+
{
3964+
EventFrame *event = FunctionToEventFrame(fp);
3965+
int64_t end = EvalContextEventStart();
3966+
assert(end >= start); /* sanity check */
3967+
event->elapsed = end - start;
3968+
SeqAppend(ctx->profiler.events, event);
3969+
}
3970+
}
3971+
3972+
int64_t EvalContextEventStart()
3973+
{
3974+
struct timespec ts;
3975+
clock_gettime(CLOCK_MONOTONIC, &ts);
3976+
return ts.tv_sec * 1000000000LL + ts.tv_nsec;
3977+
}
3978+
3979+
void EvalContextProfilingStart(EvalContext *ctx)
3980+
{
3981+
assert(ctx != NULL);
3982+
ctx->profiler.elapsed = EvalContextEventStart();
3983+
}
3984+
3985+
static HashMap *SumEventFrames(Seq *events)
3986+
{
3987+
HashMap *map = HashMapNew((MapHashFn) StringHash, (MapKeyEqualFn) StringEqual, NULL, NULL, 10);
3988+
3989+
size_t length = SeqLength(events);
3990+
EventFrame *curr;
3991+
EventFrame *prev;
3992+
MapKeyValue *mkv;
3993+
for (int i = 0; i < length; i++)
3994+
{
3995+
curr = SeqAt(events, i);
3996+
mkv = HashMapGet(map, curr->id);
3997+
3998+
if (mkv == NULL)
3999+
{
4000+
HashMapInsert(map, curr->id, curr);
4001+
continue;
4002+
}
4003+
prev = mkv->value;
4004+
prev->elapsed += curr->elapsed;
4005+
}
4006+
4007+
return map;
4008+
}
4009+
4010+
void EvalContextProfilingEnd(EvalContext *ctx, const Policy *policy)
4011+
{
4012+
assert(ctx != NULL);
4013+
4014+
if (!ctx->profiling)
4015+
{
4016+
return;
4017+
}
4018+
4019+
int64_t end = EvalContextEventStart();
4020+
int64_t start = ctx->profiler.elapsed;
4021+
assert(end >= start); /* sanity check */
4022+
4023+
ctx->profiler.elapsed = end - start;
4024+
4025+
HashMap *map = SumEventFrames(ctx->profiler.events);
4026+
JsonElement *profiling = JsonObjectCreate(2);
4027+
4028+
JsonElement *json_policy = PolicyToJson(policy);
4029+
JsonObjectAppendObject(profiling, "policy", json_policy);
4030+
4031+
JsonElement *events = JsonArrayCreate(10);
4032+
{
4033+
HashMapIterator iter = HashMapIteratorInit(map);
4034+
MapKeyValue *mkv;
4035+
while ((mkv = HashMapIteratorNext(&iter)) != NULL)
4036+
{
4037+
EventFrame *event = mkv->value;
4038+
JsonArrayAppendObject(events, EventToJson(event));
4039+
}
4040+
}
4041+
JsonObjectAppendArray(profiling, "events", events);
4042+
4043+
// write
4044+
Writer *writer = FileWriter(stdout);
4045+
JsonWrite(writer, profiling, 2);
4046+
WriterClose(writer);
4047+
4048+
JsonDestroy(profiling);
4049+
}

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)