/* *SHADOW_STACK_SIZE:Thesizeinbytesoftheentireshadowstack *SHADOW_STACK_OFFSET:Thesizeinlongwordsoftheshadowstack *SHADOW_STACK_MAX_OFFSET:Themaxoffsetofthestackforanewframetobeadded
*/ #define SHADOW_STACK_SIZE (4096) #define SHADOW_STACK_OFFSET (SHADOW_STACK_SIZE / sizeof(long)) /* Leave on a buffer at the end */ #define SHADOW_STACK_MAX_OFFSET \
(SHADOW_STACK_OFFSET - (FGRAPH_FRAME_OFFSET + 1 + FGRAPH_ARRAY_SIZE))
/* RET_STACK(): Return the frame from a given @offset from task @t */ #define RET_STACK(t, offset) ((struct ftrace_ret_stack *)(&(t)->ret_stack[offset]))
/* LRU index table for fgraph_array */ staticint fgraph_lru_table[FGRAPH_ARRAY_SIZE]; staticint fgraph_lru_next; staticint fgraph_lru_last;
/* Initialize fgraph_lru_table with unused index */ staticvoid fgraph_lru_init(void)
{ int i;
for (i = 0; i < FGRAPH_ARRAY_SIZE; i++)
fgraph_lru_table[i] = i;
}
/* Release the used index to the LRU table */ staticint fgraph_lru_release_index(int idx)
{ if (idx < 0 || idx >= FGRAPH_ARRAY_SIZE ||
WARN_ON_ONCE(fgraph_lru_table[fgraph_lru_last] != -1)) return -1;
/* Get the offset to the fgraph frame from a ret_stack value */ staticinlineint __get_offset(unsignedlong val)
{ return val & FGRAPH_FRAME_OFFSET_MASK;
}
/* Get the type of word from a ret_stack value */ staticinlineint __get_type(unsignedlong val)
{ return (val >> FGRAPH_TYPE_SHIFT) & FGRAPH_TYPE_MASK;
}
/* Get the data_index for a DATA type ret_stack word */ staticinlineint __get_data_index(unsignedlong val)
{ return (val >> FGRAPH_DATA_INDEX_SHIFT) & FGRAPH_DATA_INDEX_MASK;
}
/* Get the data_size for a DATA type ret_stack word */ staticinlineint __get_data_size(unsignedlong val)
{ return ((val >> FGRAPH_DATA_SHIFT) & FGRAPH_DATA_MASK) + 1;
}
/* Get the word from the ret_stack at @offset */ staticinlineunsignedlong get_fgraph_entry(struct task_struct *t, int offset)
{ return t->ret_stack[offset];
}
/* Get the FRAME_OFFSET from the word from the @offset on ret_stack */ staticinlineint get_frame_offset(struct task_struct *t, int offset)
{ return __get_offset(t->ret_stack[offset]);
}
/* For BITMAP type: get the bitmask from the @offset at ret_stack */ staticinlineunsignedlong
get_bitmap_bits(struct task_struct *t, int offset)
{ return (t->ret_stack[offset] >> FGRAPH_INDEX_SHIFT) & FGRAPH_INDEX_MASK;
}
/* Write the bitmap to the ret_stack at @offset (does index, offset and bitmask) */ staticinlinevoid
set_bitmap(struct task_struct *t, int offset, unsignedlong bitmap)
{
t->ret_stack[offset] = (bitmap << FGRAPH_INDEX_SHIFT) |
(FGRAPH_TYPE_BITMAP << FGRAPH_TYPE_SHIFT) | FGRAPH_FRAME_OFFSET;
}
/* For DATA type: get the data saved under the ret_stack word at @offset */ staticinlinevoid *get_data_type_data(struct task_struct *t, int offset)
{ unsignedlong val = t->ret_stack[offset];
/* Create the ret_stack word for a DATA type */ staticinlineunsignedlong make_data_type_val(int idx, int size, int offset)
{ return (idx << FGRAPH_DATA_INDEX_SHIFT) |
((size - 1) << FGRAPH_DATA_SHIFT) |
(FGRAPH_TYPE_DATA << FGRAPH_TYPE_SHIFT) | offset;
}
/* ftrace_graph_entry set to this to tell some archs to run function graph */ staticint entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops, struct ftrace_regs *fregs)
{ return0;
}
/* ftrace_graph_return set to this to tell some archs to run function graph */ staticvoid return_run(struct ftrace_graph_ret *trace, struct fgraph_ops *ops, struct ftrace_regs *fregs)
{
}
staticvoid ret_stack_set_task_var(struct task_struct *t, int idx, long val)
{ unsignedlong *gvals = SHADOW_STACK_TASK_VARS(t->ret_stack);
val = make_data_type_val(idx, data_size, __get_offset(val) + data_size + 1);
/* Set the last word to be reserved */
current->ret_stack[curr_ret_stack - 1] = val;
/* Make sure interrupts see this */
barrier();
current->curr_ret_stack = curr_ret_stack; /* Again sync with interrupts, and reset reserve */
current->ret_stack[curr_ret_stack - 1] = val;
/* Add a function return address to the trace stack on thread info.*/ staticint
ftrace_push_return_trace(unsignedlong ret, unsignedlong func, unsignedlong frame_pointer, unsignedlong *retp, int fgraph_idx)
{ struct ftrace_ret_stack *ret_stack; unsignedlong val; int offset;
if (unlikely(ftrace_graph_is_dead())) return -EBUSY;
if (!current->ret_stack) return -EBUSY;
BUILD_BUG_ON(SHADOW_STACK_SIZE % sizeof(long));
/* Set val to "reserved" with the delta to the new fgraph frame */
val = (FGRAPH_TYPE_RESERVED << FGRAPH_TYPE_SHIFT) | FGRAPH_FRAME_OFFSET;
/* *NotallarchsdefineMCOUNT_INSN_SIZEwhichisusedtolookfordirect *functions.Butthosearchscurrentlydon'tsupportdirectfunctions *anyway,andftrace_find_rec_direct()isjustastubforthem. *DefineMCOUNT_INSN_SIZEtokeepthosearchscompiling.
*/ #ifndef MCOUNT_INSN_SIZE /* Make sure this only works without direct calls */ # ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS # error MCOUNT_INSN_SIZE notdefined with direct calls enabled # endif # define MCOUNT_INSN_SIZE 0 #endif
/* If the caller does not use ftrace, call this function. */ int function_graph_enter_regs(unsignedlong ret, unsignedlong func, unsignedlong frame_pointer, unsignedlong *retp, struct ftrace_regs *fregs)
{ struct ftrace_graph_ent trace; unsignedlong bitmap = 0; int offset; int bit; int i;
bit = ftrace_test_recursion_trylock(func, ret); if (bit < 0) return -EBUSY;
/* Retrieve a function return address to the trace stack on thread info.*/ staticstruct ftrace_ret_stack *
ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsignedlong *ret, unsignedlong frame_pointer, int *offset)
{ struct ftrace_ret_stack *ret_stack;
if (unlikely(!ret_stack)) {
ftrace_graph_stop();
WARN(1, "Bad function graph ret_stack pointer: %d",
current->curr_ret_stack); /* Might as well panic, otherwise we have no where to go */
*ret = (unsignedlong)panic; return NULL;
}
/* The callbacks that hook a function */
trace_func_graph_ret_t ftrace_graph_return = ftrace_stub_graph;
trace_func_graph_ent_t ftrace_graph_entry = ftrace_graph_entry_stub;
/* Try to assign a return stack array on FTRACE_RETSTACK_ALLOC_SIZE tasks. */ staticint alloc_retstack_tasklist(unsignedlong **ret_stack_list)
{ int i; int ret = 0; int start = 0, end = FTRACE_RETSTACK_ALLOC_SIZE; struct task_struct *g, *t;
if (WARN_ON_ONCE(!fgraph_stack_cachep)) return -ENOMEM;
for (i = 0; i < FTRACE_RETSTACK_ALLOC_SIZE; i++) {
ret_stack_list[i] = kmem_cache_alloc(fgraph_stack_cachep, GFP_KERNEL); if (!ret_stack_list[i]) {
start = 0;
end = i;
ret = -ENOMEM; goto free;
}
}
rcu_read_lock();
for_each_process_thread(g, t) { if (start == end) {
ret = -EAGAIN; goto unlock;
}
if (t->ret_stack == NULL) {
atomic_set(&t->trace_overrun, 0);
ret_stack_init_task_vars(ret_stack_list[start]);
t->curr_ret_stack = 0;
t->curr_ret_depth = -1; /* Make sure the tasks see the 0 first: */
smp_wmb();
t->ret_stack = ret_stack_list[start++];
}
}
unlock:
rcu_read_unlock();
free: for (i = start; i < end; i++)
kmem_cache_free(fgraph_stack_cachep, ret_stack_list[i]); return ret;
}
/* Allocate a return stack for newly created task */ void ftrace_graph_init_task(struct task_struct *t)
{ /* Make sure we do not use the parent ret_stack */
t->ret_stack = NULL;
t->curr_ret_stack = 0;
t->curr_ret_depth = -1;
if (ftrace_graph_active) { unsignedlong *ret_stack;
/* The cpu_boot init_task->ret_stack will never be freed */
for_each_online_cpu(cpu) { if (!idle_task(cpu)->ret_stack)
ftrace_graph_init_idle_task(idle_task(cpu), cpu);
}
do {
ret = alloc_retstack_tasklist(ret_stack_list);
} while (ret == -EAGAIN);
if (!ret) {
ret = register_trace_sched_switch(ftrace_graph_probe_sched_switch, NULL); if (ret)
pr_info("ftrace_graph: Couldn't activate tracepoint" " probe to kernel_sched_switch\n");
}
kfree(ret_stack_list); return ret;
}
staticvoid init_task_vars(int idx)
{ struct task_struct *g, *t; int cpu;
for_each_online_cpu(cpu) { if (idle_task(cpu)->ret_stack)
ret_stack_set_task_var(idle_task(cpu), idx, 0);
}
/* The cpu_boot init_task->ret_stack will never be freed */ staticint fgraph_cpu_init(unsignedint cpu)
{ if (!idle_task(cpu)->ret_stack)
ftrace_graph_init_idle_task(idle_task(cpu), cpu); return0;
}
int register_ftrace_graph(struct fgraph_ops *gops)
{ staticbool fgraph_initialized; int command = 0; int ret = 0; int i = -1;
if (!fgraph_stack_cachep) {
fgraph_stack_cachep = kmem_cache_create("fgraph_stack",
SHADOW_STACK_SIZE,
SHADOW_STACK_SIZE, 0, NULL); if (!fgraph_stack_cachep) return -ENOMEM;
}
if (!fgraph_initialized) {
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "fgraph:online",
fgraph_cpu_init, NULL); if (ret < 0) {
pr_warn("fgraph: Error to init cpu hotplug support\n"); return ret;
}
fgraph_initialized = true;
ret = 0;
}
if (!fgraph_array[0]) { /* The array must always have real data on it */ for (i = 0; i < FGRAPH_ARRAY_SIZE; i++)
fgraph_array[i] = &fgraph_stub;
fgraph_lru_init();
}
i = fgraph_lru_alloc_index(); if (i < 0 || WARN_ON_ONCE(fgraph_array[i] != &fgraph_stub)) return -ENOSPC;
gops->idx = i;
ftrace_graph_active++;
if (ftrace_graph_active == 2)
ftrace_graph_disable_direct(true);
if (ftrace_graph_active == 1) {
ftrace_graph_enable_direct(false, gops);
register_pm_notifier(&ftrace_suspend_notifier);
ret = start_graph_tracing(); if (ret) goto error; /* *Somearchsjusttesttoseeifthesearenot *thedefaultfunction
*/
ftrace_graph_return = return_run;
ftrace_graph_entry = entry_run;
command = FTRACE_START_FUNC_RET;
} else {
init_task_vars(gops->idx);
} /* Always save the function, and reset at unregistering */
gops->saved_func = gops->entryfunc;
gops->ops.flags |= FTRACE_OPS_FL_GRAPH;
ret = ftrace_startup_subops(&graph_ops, &gops->ops, command); if (!ret)
fgraph_array[i] = gops;
error: if (ret) {
ftrace_graph_active--;
gops->saved_func = NULL;
fgraph_lru_release_index(i); if (!ftrace_graph_active)
unregister_pm_notifier(&ftrace_suspend_notifier);
} return ret;
}
void unregister_ftrace_graph(struct fgraph_ops *gops)
{ int command = 0;
if (WARN_ONCE(!(gops->ops.flags & FTRACE_OPS_FL_GRAPH), "function graph ops unregistered without registering")) return;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.