diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/gcov/Kconfig | 8 | ||||
-rw-r--r-- | kernel/gcov/gcc_3_4.c | 88 | ||||
-rw-r--r-- | kernel/gcov/gcov.h | 42 | ||||
-rw-r--r-- | kernel/module.c | 2 | ||||
-rw-r--r-- | kernel/trace/Kconfig | 9 | ||||
-rw-r--r-- | kernel/trace/Makefile | 1 | ||||
-rw-r--r-- | kernel/trace/tracelevel.c | 142 |
7 files changed, 271 insertions, 21 deletions
diff --git a/kernel/gcov/Kconfig b/kernel/gcov/Kconfig index a92028196cc1..824b741925bb 100644 --- a/kernel/gcov/Kconfig +++ b/kernel/gcov/Kconfig @@ -35,7 +35,7 @@ config GCOV_KERNEL config GCOV_PROFILE_ALL bool "Profile entire Kernel" depends on GCOV_KERNEL - depends on SUPERH || S390 || X86 || (PPC && EXPERIMENTAL) || MICROBLAZE + depends on SUPERH || S390 || X86 || (PPC && EXPERIMENTAL) || MICROBLAZE || ARM default n ---help--- This options activates profiling for the entire kernel. @@ -46,4 +46,10 @@ config GCOV_PROFILE_ALL larger and run slower. Also be sure to exclude files from profiling which are not linked to the kernel image to prevent linker errors. +config GCOV_CTORS + string + depends on CONSTRUCTORS + default ".init_array" if ARM && AEABI + default ".ctors" + endmenu diff --git a/kernel/gcov/gcc_3_4.c b/kernel/gcov/gcc_3_4.c index ae5bb4260033..d753d1152b7b 100644 --- a/kernel/gcov/gcc_3_4.c +++ b/kernel/gcov/gcc_3_4.c @@ -297,16 +297,30 @@ void gcov_iter_start(struct gcov_iterator *iter) } /* Mapping of logical record number to actual file content. */ -#define RECORD_FILE_MAGIC 0 -#define RECORD_GCOV_VERSION 1 -#define RECORD_TIME_STAMP 2 -#define RECORD_FUNCTION_TAG 3 -#define RECORD_FUNCTON_TAG_LEN 4 -#define RECORD_FUNCTION_IDENT 5 -#define RECORD_FUNCTION_CHECK 6 -#define RECORD_COUNT_TAG 7 -#define RECORD_COUNT_LEN 8 -#define RECORD_COUNT 9 +#define RECORD_FILE_MAGIC 0 +#define RECORD_GCOV_VERSION 1 +#define RECORD_TIME_STAMP 2 +#define RECORD_FUNCTION_TAG 3 +#define RECORD_FUNCTON_TAG_LEN 4 +#define RECORD_FUNCTION_IDENT 5 +#define RECORD_FUNCTION_CHECK_LINE 6 +#define RECORD_FUNCTION_CHECK_CFG 7 +#define RECORD_FUNCTION_NAME_LEN 8 +#define RECORD_FUNCTION_NAME 9 +#define RECORD_COUNT_TAG 10 +#define RECORD_COUNT_LEN 11 +#define RECORD_COUNT 12 + +/* Return length of string encoded in GCOV format. */ +static size_t +sizeof_str(const char *str) +{ + size_t len; + len = (str) ? strlen(str) : 0; + if (len == 0) + return 1; + return 1 + ((len + 4) >> 2); +} /** * gcov_iter_next - advance file iterator to next logical record @@ -323,6 +337,9 @@ int gcov_iter_next(struct gcov_iterator *iter) case RECORD_FUNCTON_TAG_LEN: case RECORD_FUNCTION_IDENT: case RECORD_COUNT_TAG: + case RECORD_FUNCTION_CHECK_LINE: + case RECORD_FUNCTION_CHECK_CFG: + case RECORD_FUNCTION_NAME_LEN: /* Advance to next record */ iter->record++; break; @@ -332,7 +349,7 @@ int gcov_iter_next(struct gcov_iterator *iter) /* fall through */ case RECORD_COUNT_LEN: if (iter->count < get_func(iter)->n_ctrs[iter->type]) { - iter->record = 9; + iter->record = 12; break; } /* Advance to next counter type */ @@ -340,9 +357,9 @@ int gcov_iter_next(struct gcov_iterator *iter) iter->count = 0; iter->type++; /* fall through */ - case RECORD_FUNCTION_CHECK: + case RECORD_FUNCTION_NAME: if (iter->type < iter->num_types) { - iter->record = 7; + iter->record = 10; break; } /* Advance to next function */ @@ -395,6 +412,34 @@ static int seq_write_gcov_u64(struct seq_file *seq, u64 v) data[1] = (v >> 32); return seq_write(seq, data, sizeof(data)); } +/** + * seq_write_gcov_str - write string in gcov format to seq_file + * @seq: seq_file handle + * @str: string to be stored + * + * Number format defined by gcc: numbers are recorded in the 32 bit + * unsigned binary form of the endianness of the machine generating the + * file. 64 bit numbers are stored as two 32 bit numbers, the low part + * first. + */ +static int seq_write_gcov_str(struct seq_file *seq, const char *str) +{ + if (str) { + size_t len; + int str_off; + u32 data; + len = strlen(str); + for (str_off = 0; str_off < (sizeof_str(str) - 2) ; str_off++) { + memcpy(&data, (str + str_off * 4), 4); + seq_write(seq, &data, sizeof(data)); + } + data = 0; + memcpy(&data, (str + str_off * 4), (len - str_off * 4)); + return seq_write(seq, &data, sizeof(data)); + } else { + return 0; + } +} /** * gcov_iter_write - write data for current pos to seq_file @@ -421,13 +466,24 @@ int gcov_iter_write(struct gcov_iterator *iter, struct seq_file *seq) rc = seq_write_gcov_u32(seq, GCOV_TAG_FUNCTION); break; case RECORD_FUNCTON_TAG_LEN: - rc = seq_write_gcov_u32(seq, 2); + rc = seq_write_gcov_u32(seq, GCOV_TAG_FUNCTION_LENGTH + + (sizeof_str(get_func(iter)->name))); break; case RECORD_FUNCTION_IDENT: rc = seq_write_gcov_u32(seq, get_func(iter)->ident); break; - case RECORD_FUNCTION_CHECK: - rc = seq_write_gcov_u32(seq, get_func(iter)->checksum); + case RECORD_FUNCTION_CHECK_LINE: + rc = seq_write_gcov_u32(seq, get_func(iter)->lineno_checksum); + break; + case RECORD_FUNCTION_CHECK_CFG: + rc = seq_write_gcov_u32(seq, get_func(iter)->cfg_checksum); + break; + case RECORD_FUNCTION_NAME_LEN: + rc = seq_write_gcov_u32(seq, + (sizeof_str(get_func(iter)->name) - 1)); + break; + case RECORD_FUNCTION_NAME: + rc = seq_write_gcov_str(seq, get_func(iter)->name); break; case RECORD_COUNT_TAG: rc = seq_write_gcov_u32(seq, diff --git a/kernel/gcov/gcov.h b/kernel/gcov/gcov.h index 060073ebf7a6..040c6980df0d 100644 --- a/kernel/gcov/gcov.h +++ b/kernel/gcov/gcov.h @@ -21,9 +21,10 @@ * gcc and need to be kept as close to the original definition as possible to * remain compatible. */ -#define GCOV_COUNTERS 5 +#define GCOV_COUNTERS 10 #define GCOV_DATA_MAGIC ((unsigned int) 0x67636461) #define GCOV_TAG_FUNCTION ((unsigned int) 0x01000000) +#define GCOV_TAG_FUNCTION_LENGTH 3 #define GCOV_TAG_COUNTER_BASE ((unsigned int) 0x01a10000) #define GCOV_TAG_FOR_COUNTER(count) \ (GCOV_TAG_COUNTER_BASE + ((unsigned int) (count) << 17)) @@ -34,10 +35,38 @@ typedef long gcov_type; typedef long long gcov_type; #endif +/* + * Source module info. The data structure is used in both runtime and + * profile-use phase. + */ +struct gcov_module_info { + unsigned int ident; +/* + * This is overloaded to mean two things: + * (1) means FDO/LIPO in instrumented binary. + * (2) means IS_PRIMARY in persistent file or memory copy used in profile-use. + */ + unsigned int is_primary; + unsigned int is_exported; + unsigned int lang; + char *da_filename; + char *source_filename; + unsigned int num_quote_paths; + unsigned int num_bracket_paths; + unsigned int num_cpp_defines; + unsigned int num_cpp_includes; + unsigned int num_cl_args; + char *string_array[1]; +}; + + /** * struct gcov_fn_info - profiling meta data per function * @ident: object file-unique function identifier - * @checksum: function checksum + * @lineno_checksum: function lineno checksum + * @cfg_checksum: function cfg checksum + * @dc_offset: direct call offset + * @name: function name * @n_ctrs: number of values per counter type belonging to this function * * This data is generated by gcc during compilation and doesn't change @@ -45,7 +74,10 @@ typedef long long gcov_type; */ struct gcov_fn_info { unsigned int ident; - unsigned int checksum; + unsigned int lineno_checksum; + unsigned int cfg_checksum; + unsigned int dc_offset; + const char *name; unsigned int n_ctrs[0]; }; @@ -67,9 +99,11 @@ struct gcov_ctr_info { /** * struct gcov_info - profiling data per object file * @version: gcov version magic indicating the gcc version used for compilation + * @modinfo: additional module information * @next: list head for a singly-linked list * @stamp: time stamp * @filename: name of the associated gcov data file + * @eof_pos: end position of profile data * @n_functions: number of instrumented functions * @functions: function data * @ctr_mask: mask specifying which counter types are active @@ -80,9 +114,11 @@ struct gcov_ctr_info { */ struct gcov_info { unsigned int version; + struct gcov_module_info *mod_info; struct gcov_info *next; unsigned int stamp; const char *filename; + unsigned int eof_pos; unsigned int n_functions; const struct gcov_fn_info *functions; unsigned int ctr_mask; diff --git a/kernel/module.c b/kernel/module.c index 04379f92f843..e0ddcece2be4 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -2528,7 +2528,7 @@ static void find_module_sections(struct module *mod, struct load_info *info) mod->unused_gpl_crcs = section_addr(info, "__kcrctab_unused_gpl"); #endif #ifdef CONFIG_CONSTRUCTORS - mod->ctors = section_objs(info, ".ctors", + mod->ctors = section_objs(info, CONFIG_GCOV_CTORS, sizeof(*mod->ctors), &mod->num_ctors); #endif diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index cd3134510f3d..997ef6077e9e 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig @@ -487,6 +487,15 @@ config RING_BUFFER_BENCHMARK If unsure, say N. +config TRACELEVEL + bool "Add capability to prioritize traces" + depends on EVENT_TRACING + help + This option allows subsystem programmers to add priorities to trace + events by calling to tracelevel_register. Traces of high priority + will automatically be enabled on kernel boot, and users can change + the the trace level in a kernel parameter. + endif # FTRACE endif # TRACING_SUPPORT diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index 761c510a06c5..c3462a5ce058 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile @@ -56,5 +56,6 @@ obj-$(CONFIG_TRACEPOINTS) += power-traces.o ifeq ($(CONFIG_TRACING),y) obj-$(CONFIG_KGDB_KDB) += trace_kdb.o endif +obj-$(CONFIG_TRACELEVEL) += tracelevel.o libftrace-y := ftrace.o diff --git a/kernel/trace/tracelevel.c b/kernel/trace/tracelevel.c new file mode 100644 index 000000000000..9f8b8eedbb58 --- /dev/null +++ b/kernel/trace/tracelevel.c @@ -0,0 +1,142 @@ +/* + * kernel/trace/tracelevel.c + * + * Copyright (c) 2011, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <linux/ftrace_event.h> +#include <linux/list.h> +#include <linux/moduleparam.h> +#include <linux/mutex.h> +#include <linux/tracelevel.h> +#include <linux/vmalloc.h> + +#include "trace.h" + +#define TAG KERN_ERR "tracelevel: " + +struct tracelevel_record { + struct list_head list; + char *name; + int level; +}; + +static LIST_HEAD(tracelevel_list); + +static bool started; +static unsigned int tracelevel_level = TRACELEVEL_DEFAULT; + +static DEFINE_MUTEX(tracelevel_record_lock); + +/* tracelevel_set_event sets a single event if set = 1, or + * clears an event if set = 0. + */ +static int tracelevel_set_event(struct tracelevel_record *evt, bool set) +{ + if (trace_set_clr_event(NULL, evt->name, set) < 0) { + printk(TAG "failed to set event %s\n", evt->name); + return -EINVAL; + } + return 0; +} + +/* Registers an event. If possible, it also sets it. + * If not, we'll set it in tracelevel_init. + */ +int __tracelevel_register(char *name, unsigned int level) +{ + struct tracelevel_record *evt = (struct tracelevel_record *) + vmalloc(sizeof(struct tracelevel_record)); + if (!evt) { + printk(TAG "failed to allocate tracelevel_record for %s\n", + name); + return -ENOMEM; + } + + evt->name = name; + evt->level = level; + + mutex_lock(&tracelevel_record_lock); + list_add(&evt->list, &tracelevel_list); + mutex_unlock(&tracelevel_record_lock); + + if (level >= tracelevel_level && started) + tracelevel_set_event(evt, 1); + return 0; +} + +/* tracelevel_set_level sets the global level, clears events + * lower than that level, and enables events greater or equal. + */ +int tracelevel_set_level(int level) +{ + struct tracelevel_record *evt = NULL; + + if (level < 0 || level > TRACELEVEL_MAX) + return -EINVAL; + tracelevel_level = level; + + mutex_lock(&tracelevel_record_lock); + list_for_each_entry(evt, &tracelevel_list, list) { + if (evt->level >= level) + tracelevel_set_event(evt, 1); + else + tracelevel_set_event(evt, 0); + } + mutex_unlock(&tracelevel_record_lock); + return 0; +} + +static int param_set_level(const char *val, const struct kernel_param *kp) +{ + int level, ret; + ret = strict_strtol(val, 0, &level); + if (ret < 0) + return ret; + return tracelevel_set_level(level); +} + +static int param_get_level(char *buffer, const struct kernel_param *kp) +{ + return param_get_int(buffer, kp); +} + +static struct kernel_param_ops tracelevel_level_ops = { + .set = param_set_level, + .get = param_get_level +}; + +module_param_cb(level, &tracelevel_level_ops, &tracelevel_level, 0644); + +/* Turn on the tracing that has been registered thus far. */ +static int __init tracelevel_init(void) +{ + int ret; + started = true; + + /* Ring buffer is initialize to 1 page until the user sets a tracer. + * Since we're doing this manually, we need to ask for expanded buffer. + */ + ret = tracing_update_buffers(); + if (ret < 0) + return ret; + + return tracelevel_set_level(tracelevel_level); +} + +/* Tracing mechanism is set up during fs_initcall. */ +fs_initcall_sync(tracelevel_init); |