Skip to content

Commit a35873a

Browse files
tzanussirostedt
authored andcommitted
tracing: Add conditional snapshot
Currently, tracing snapshots are context-free - they capture the ring buffer contents at the time the tracing_snapshot() function was invoked, and nothing else. Additionally, they're always taken unconditionally - the calling code can decide whether or not to take a snapshot, but the data used to make that decision is kept separately from the snapshot itself. This change adds the ability to associate with each trace instance some user data, along with an 'update' function that can use that data to determine whether or not to actually take a snapshot. The update function can then update that data along with any other state (as part of the data presumably), if warranted. Because snapshots are 'global' per-instance, only one user can enable and use a conditional snapshot for any given trace instance. To enable a conditional snapshot (see details in the function and data structure comments), the user calls tracing_snapshot_cond_enable(). Similarly, to disable a conditional snapshot and free it up for other users, tracing_snapshot_cond_disable() should be called. To actually initiate a conditional snapshot, tracing_snapshot_cond() should be called. tracing_snapshot_cond() will invoke the update() callback, allowing the user to decide whether or not to actually take the snapshot and update the user-defined data associated with the snapshot. If the callback returns 'true', tracing_snapshot_cond() will then actually take the snapshot and return. This scheme allows for flexibility in snapshot implementations - for example, by implementing slightly different update() callbacks, snapshots can be taken in situations where the user is only interested in taking a snapshot when a new maximum in hit versus when a value changes in any way at all. Future patches will demonstrate both cases. Link: http://lkml.kernel.org/r/1bea07828d5fd6864a585f83b1eed47ce097eb45.1550100284.git.tom.zanussi@linux.intel.com Signed-off-by: Tom Zanussi <[email protected]> Signed-off-by: Steven Rostedt (VMware) <[email protected]>
1 parent 466f452 commit a35873a

File tree

3 files changed

+244
-6
lines changed

3 files changed

+244
-6
lines changed

kernel/trace/trace.c

Lines changed: 188 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -894,7 +894,7 @@ int __trace_bputs(unsigned long ip, const char *str)
894894
EXPORT_SYMBOL_GPL(__trace_bputs);
895895

896896
#ifdef CONFIG_TRACER_SNAPSHOT
897-
void tracing_snapshot_instance(struct trace_array *tr)
897+
void tracing_snapshot_instance_cond(struct trace_array *tr, void *cond_data)
898898
{
899899
struct tracer *tracer = tr->current_trace;
900900
unsigned long flags;
@@ -920,10 +920,15 @@ void tracing_snapshot_instance(struct trace_array *tr)
920920
}
921921

922922
local_irq_save(flags);
923-
update_max_tr(tr, current, smp_processor_id());
923+
update_max_tr(tr, current, smp_processor_id(), cond_data);
924924
local_irq_restore(flags);
925925
}
926926

927+
void tracing_snapshot_instance(struct trace_array *tr)
928+
{
929+
tracing_snapshot_instance_cond(tr, NULL);
930+
}
931+
927932
/**
928933
* tracing_snapshot - take a snapshot of the current buffer.
929934
*
@@ -946,6 +951,54 @@ void tracing_snapshot(void)
946951
}
947952
EXPORT_SYMBOL_GPL(tracing_snapshot);
948953

954+
/**
955+
* tracing_snapshot_cond - conditionally take a snapshot of the current buffer.
956+
* @tr: The tracing instance to snapshot
957+
* @cond_data: The data to be tested conditionally, and possibly saved
958+
*
959+
* This is the same as tracing_snapshot() except that the snapshot is
960+
* conditional - the snapshot will only happen if the
961+
* cond_snapshot.update() implementation receiving the cond_data
962+
* returns true, which means that the trace array's cond_snapshot
963+
* update() operation used the cond_data to determine whether the
964+
* snapshot should be taken, and if it was, presumably saved it along
965+
* with the snapshot.
966+
*/
967+
void tracing_snapshot_cond(struct trace_array *tr, void *cond_data)
968+
{
969+
tracing_snapshot_instance_cond(tr, cond_data);
970+
}
971+
EXPORT_SYMBOL_GPL(tracing_snapshot_cond);
972+
973+
/**
974+
* tracing_snapshot_cond_data - get the user data associated with a snapshot
975+
* @tr: The tracing instance
976+
*
977+
* When the user enables a conditional snapshot using
978+
* tracing_snapshot_cond_enable(), the user-defined cond_data is saved
979+
* with the snapshot. This accessor is used to retrieve it.
980+
*
981+
* Should not be called from cond_snapshot.update(), since it takes
982+
* the tr->max_lock lock, which the code calling
983+
* cond_snapshot.update() has already done.
984+
*
985+
* Returns the cond_data associated with the trace array's snapshot.
986+
*/
987+
void *tracing_cond_snapshot_data(struct trace_array *tr)
988+
{
989+
void *cond_data = NULL;
990+
991+
arch_spin_lock(&tr->max_lock);
992+
993+
if (tr->cond_snapshot)
994+
cond_data = tr->cond_snapshot->cond_data;
995+
996+
arch_spin_unlock(&tr->max_lock);
997+
998+
return cond_data;
999+
}
1000+
EXPORT_SYMBOL_GPL(tracing_cond_snapshot_data);
1001+
9491002
static int resize_buffer_duplicate_size(struct trace_buffer *trace_buf,
9501003
struct trace_buffer *size_buf, int cpu_id);
9511004
static void set_buffer_entries(struct trace_buffer *buf, unsigned long val);
@@ -1025,12 +1078,103 @@ void tracing_snapshot_alloc(void)
10251078
tracing_snapshot();
10261079
}
10271080
EXPORT_SYMBOL_GPL(tracing_snapshot_alloc);
1081+
1082+
/**
1083+
* tracing_snapshot_cond_enable - enable conditional snapshot for an instance
1084+
* @tr: The tracing instance
1085+
* @cond_data: User data to associate with the snapshot
1086+
* @update: Implementation of the cond_snapshot update function
1087+
*
1088+
* Check whether the conditional snapshot for the given instance has
1089+
* already been enabled, or if the current tracer is already using a
1090+
* snapshot; if so, return -EBUSY, else create a cond_snapshot and
1091+
* save the cond_data and update function inside.
1092+
*
1093+
* Returns 0 if successful, error otherwise.
1094+
*/
1095+
int tracing_snapshot_cond_enable(struct trace_array *tr, void *cond_data,
1096+
cond_update_fn_t update)
1097+
{
1098+
struct cond_snapshot *cond_snapshot;
1099+
int ret = 0;
1100+
1101+
cond_snapshot = kzalloc(sizeof(*cond_snapshot), GFP_KERNEL);
1102+
if (!cond_snapshot)
1103+
return -ENOMEM;
1104+
1105+
cond_snapshot->cond_data = cond_data;
1106+
cond_snapshot->update = update;
1107+
1108+
mutex_lock(&trace_types_lock);
1109+
1110+
ret = tracing_alloc_snapshot_instance(tr);
1111+
if (ret)
1112+
goto fail_unlock;
1113+
1114+
if (tr->current_trace->use_max_tr) {
1115+
ret = -EBUSY;
1116+
goto fail_unlock;
1117+
}
1118+
1119+
if (tr->cond_snapshot) {
1120+
ret = -EBUSY;
1121+
goto fail_unlock;
1122+
}
1123+
1124+
arch_spin_lock(&tr->max_lock);
1125+
tr->cond_snapshot = cond_snapshot;
1126+
arch_spin_unlock(&tr->max_lock);
1127+
1128+
mutex_unlock(&trace_types_lock);
1129+
1130+
return ret;
1131+
1132+
fail_unlock:
1133+
mutex_unlock(&trace_types_lock);
1134+
kfree(cond_snapshot);
1135+
return ret;
1136+
}
1137+
EXPORT_SYMBOL_GPL(tracing_snapshot_cond_enable);
1138+
1139+
/**
1140+
* tracing_snapshot_cond_disable - disable conditional snapshot for an instance
1141+
* @tr: The tracing instance
1142+
*
1143+
* Check whether the conditional snapshot for the given instance is
1144+
* enabled; if so, free the cond_snapshot associated with it,
1145+
* otherwise return -EINVAL.
1146+
*
1147+
* Returns 0 if successful, error otherwise.
1148+
*/
1149+
int tracing_snapshot_cond_disable(struct trace_array *tr)
1150+
{
1151+
int ret = 0;
1152+
1153+
arch_spin_lock(&tr->max_lock);
1154+
1155+
if (!tr->cond_snapshot)
1156+
ret = -EINVAL;
1157+
else {
1158+
kfree(tr->cond_snapshot);
1159+
tr->cond_snapshot = NULL;
1160+
}
1161+
1162+
arch_spin_unlock(&tr->max_lock);
1163+
1164+
return ret;
1165+
}
1166+
EXPORT_SYMBOL_GPL(tracing_snapshot_cond_disable);
10281167
#else
10291168
void tracing_snapshot(void)
10301169
{
10311170
WARN_ONCE(1, "Snapshot feature not enabled, but internal snapshot used");
10321171
}
10331172
EXPORT_SYMBOL_GPL(tracing_snapshot);
1173+
void tracing_snapshot_cond(struct trace_array *tr, void *cond_data)
1174+
{
1175+
WARN_ONCE(1, "Snapshot feature not enabled, but internal conditional snapshot used");
1176+
}
1177+
EXPORT_SYMBOL_GPL(tracing_snapshot_cond);
10341178
int tracing_alloc_snapshot(void)
10351179
{
10361180
WARN_ONCE(1, "Snapshot feature not enabled, but snapshot allocation used");
@@ -1043,6 +1187,21 @@ void tracing_snapshot_alloc(void)
10431187
tracing_snapshot();
10441188
}
10451189
EXPORT_SYMBOL_GPL(tracing_snapshot_alloc);
1190+
void *tracing_cond_snapshot_data(struct trace_array *tr)
1191+
{
1192+
return NULL;
1193+
}
1194+
EXPORT_SYMBOL_GPL(tracing_cond_snapshot_data);
1195+
int tracing_snapshot_cond_enable(struct trace_array *tr, void *cond_data, cond_update_fn_t update)
1196+
{
1197+
return -ENODEV;
1198+
}
1199+
EXPORT_SYMBOL_GPL(tracing_snapshot_cond_enable);
1200+
int tracing_snapshot_cond_disable(struct trace_array *tr)
1201+
{
1202+
return false;
1203+
}
1204+
EXPORT_SYMBOL_GPL(tracing_snapshot_cond_disable);
10461205
#endif /* CONFIG_TRACER_SNAPSHOT */
10471206

10481207
void tracer_tracing_off(struct trace_array *tr)
@@ -1354,12 +1513,14 @@ __update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu)
13541513
* @tr: tracer
13551514
* @tsk: the task with the latency
13561515
* @cpu: The cpu that initiated the trace.
1516+
* @cond_data: User data associated with a conditional snapshot
13571517
*
13581518
* Flip the buffers between the @tr and the max_tr and record information
13591519
* about which task was the cause of this latency.
13601520
*/
13611521
void
1362-
update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu)
1522+
update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu,
1523+
void *cond_data)
13631524
{
13641525
if (tr->stop_count)
13651526
return;
@@ -1380,9 +1541,15 @@ update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu)
13801541
else
13811542
ring_buffer_record_off(tr->max_buffer.buffer);
13821543

1544+
#ifdef CONFIG_TRACER_SNAPSHOT
1545+
if (tr->cond_snapshot && !tr->cond_snapshot->update(tr, cond_data))
1546+
goto out_unlock;
1547+
#endif
13831548
swap(tr->trace_buffer.buffer, tr->max_buffer.buffer);
13841549

13851550
__update_max_tr(tr, tsk, cpu);
1551+
1552+
out_unlock:
13861553
arch_spin_unlock(&tr->max_lock);
13871554
}
13881555

@@ -5396,6 +5563,16 @@ static int tracing_set_tracer(struct trace_array *tr, const char *buf)
53965563
if (t == tr->current_trace)
53975564
goto out;
53985565

5566+
#ifdef CONFIG_TRACER_SNAPSHOT
5567+
if (t->use_max_tr) {
5568+
arch_spin_lock(&tr->max_lock);
5569+
if (tr->cond_snapshot)
5570+
ret = -EBUSY;
5571+
arch_spin_unlock(&tr->max_lock);
5572+
if (ret)
5573+
goto out;
5574+
}
5575+
#endif
53995576
/* Some tracers won't work on kernel command line */
54005577
if (system_state < SYSTEM_RUNNING && t->noboot) {
54015578
pr_warn("Tracer '%s' is not allowed on command line, ignored\n",
@@ -6477,6 +6654,13 @@ tracing_snapshot_write(struct file *filp, const char __user *ubuf, size_t cnt,
64776654
goto out;
64786655
}
64796656

6657+
arch_spin_lock(&tr->max_lock);
6658+
if (tr->cond_snapshot)
6659+
ret = -EBUSY;
6660+
arch_spin_unlock(&tr->max_lock);
6661+
if (ret)
6662+
goto out;
6663+
64806664
switch (val) {
64816665
case 0:
64826666
if (iter->cpu_file != RING_BUFFER_ALL_CPUS) {
@@ -6502,7 +6686,7 @@ tracing_snapshot_write(struct file *filp, const char __user *ubuf, size_t cnt,
65026686
local_irq_disable();
65036687
/* Now, we're going to swap */
65046688
if (iter->cpu_file == RING_BUFFER_ALL_CPUS)
6505-
update_max_tr(tr, current, smp_processor_id());
6689+
update_max_tr(tr, current, smp_processor_id(), NULL);
65066690
else
65076691
update_max_tr_single(tr, current, iter->cpu_file);
65086692
local_irq_enable();

kernel/trace/trace.h

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,51 @@ struct trace_pid_list {
194194
unsigned long *pids;
195195
};
196196

197+
typedef bool (*cond_update_fn_t)(struct trace_array *tr, void *cond_data);
198+
199+
/**
200+
* struct cond_snapshot - conditional snapshot data and callback
201+
*
202+
* The cond_snapshot structure encapsulates a callback function and
203+
* data associated with the snapshot for a given tracing instance.
204+
*
205+
* When a snapshot is taken conditionally, by invoking
206+
* tracing_snapshot_cond(tr, cond_data), the cond_data passed in is
207+
* passed in turn to the cond_snapshot.update() function. That data
208+
* can be compared by the update() implementation with the cond_data
209+
* contained wihin the struct cond_snapshot instance associated with
210+
* the trace_array. Because the tr->max_lock is held throughout the
211+
* update() call, the update() function can directly retrieve the
212+
* cond_snapshot and cond_data associated with the per-instance
213+
* snapshot associated with the trace_array.
214+
*
215+
* The cond_snapshot.update() implementation can save data to be
216+
* associated with the snapshot if it decides to, and returns 'true'
217+
* in that case, or it returns 'false' if the conditional snapshot
218+
* shouldn't be taken.
219+
*
220+
* The cond_snapshot instance is created and associated with the
221+
* user-defined cond_data by tracing_cond_snapshot_enable().
222+
* Likewise, the cond_snapshot instance is destroyed and is no longer
223+
* associated with the trace instance by
224+
* tracing_cond_snapshot_disable().
225+
*
226+
* The method below is required.
227+
*
228+
* @update: When a conditional snapshot is invoked, the update()
229+
* callback function is invoked with the tr->max_lock held. The
230+
* update() implementation signals whether or not to actually
231+
* take the snapshot, by returning 'true' if so, 'false' if no
232+
* snapshot should be taken. Because the max_lock is held for
233+
* the duration of update(), the implementation is safe to
234+
* directly retrieven and save any implementation data it needs
235+
* to in association with the snapshot.
236+
*/
237+
struct cond_snapshot {
238+
void *cond_data;
239+
cond_update_fn_t update;
240+
};
241+
197242
/*
198243
* The trace array - an array of per-CPU trace arrays. This is the
199244
* highest level data structure that individual tracers deal with.
@@ -277,6 +322,9 @@ struct trace_array {
277322
#endif
278323
int time_stamp_abs_ref;
279324
struct list_head hist_vars;
325+
#ifdef CONFIG_TRACER_SNAPSHOT
326+
struct cond_snapshot *cond_snapshot;
327+
#endif
280328
};
281329

282330
enum {
@@ -727,7 +775,8 @@ int trace_pid_write(struct trace_pid_list *filtered_pids,
727775
const char __user *ubuf, size_t cnt);
728776

729777
#ifdef CONFIG_TRACER_MAX_TRACE
730-
void update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu);
778+
void update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu,
779+
void *cond_data);
731780
void update_max_tr_single(struct trace_array *tr,
732781
struct task_struct *tsk, int cpu);
733782
#endif /* CONFIG_TRACER_MAX_TRACE */
@@ -1810,6 +1859,11 @@ static inline bool event_command_needs_rec(struct event_command *cmd_ops)
18101859
extern int trace_event_enable_disable(struct trace_event_file *file,
18111860
int enable, int soft_disable);
18121861
extern int tracing_alloc_snapshot(void);
1862+
extern void tracing_snapshot_cond(struct trace_array *tr, void *cond_data);
1863+
extern int tracing_snapshot_cond_enable(struct trace_array *tr, void *cond_data, cond_update_fn_t update);
1864+
1865+
extern int tracing_snapshot_cond_disable(struct trace_array *tr);
1866+
extern void *tracing_cond_snapshot_data(struct trace_array *tr);
18131867

18141868
extern const char *__start___trace_bprintk_fmt[];
18151869
extern const char *__stop___trace_bprintk_fmt[];

kernel/trace/trace_sched_wakeup.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,7 @@ probe_wakeup_sched_switch(void *ignore, bool preempt,
486486

487487
if (likely(!is_tracing_stopped())) {
488488
wakeup_trace->max_latency = delta;
489-
update_max_tr(wakeup_trace, wakeup_task, wakeup_cpu);
489+
update_max_tr(wakeup_trace, wakeup_task, wakeup_cpu, NULL);
490490
}
491491

492492
out_unlock:

0 commit comments

Comments
 (0)