summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/gcov/Kconfig8
-rw-r--r--kernel/gcov/gcc_3_4.c88
-rw-r--r--kernel/gcov/gcov.h42
-rw-r--r--kernel/module.c2
-rw-r--r--kernel/trace/Kconfig9
-rw-r--r--kernel/trace/Makefile1
-rw-r--r--kernel/trace/tracelevel.c142
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);