diff options
Diffstat (limited to 'kernel/gcov')
-rw-r--r-- | kernel/gcov/Kconfig | 10 | ||||
-rw-r--r-- | kernel/gcov/gcc_3_4.c | 88 | ||||
-rw-r--r-- | kernel/gcov/gcov.h | 42 |
3 files changed, 119 insertions, 21 deletions
diff --git a/kernel/gcov/Kconfig b/kernel/gcov/Kconfig index 5bf924d80b5..824b741925b 100644 --- a/kernel/gcov/Kconfig +++ b/kernel/gcov/Kconfig | |||
@@ -3,7 +3,7 @@ menu "GCOV-based kernel profiling" | |||
3 | config GCOV_KERNEL | 3 | config GCOV_KERNEL |
4 | bool "Enable gcov-based kernel profiling" | 4 | bool "Enable gcov-based kernel profiling" |
5 | depends on DEBUG_FS | 5 | depends on DEBUG_FS |
6 | select CONSTRUCTORS | 6 | select CONSTRUCTORS if !UML |
7 | default n | 7 | default n |
8 | ---help--- | 8 | ---help--- |
9 | This option enables gcov-based code profiling (e.g. for code coverage | 9 | This option enables gcov-based code profiling (e.g. for code coverage |
@@ -35,7 +35,7 @@ config GCOV_KERNEL | |||
35 | config GCOV_PROFILE_ALL | 35 | config GCOV_PROFILE_ALL |
36 | bool "Profile entire Kernel" | 36 | bool "Profile entire Kernel" |
37 | depends on GCOV_KERNEL | 37 | depends on GCOV_KERNEL |
38 | depends on SUPERH || S390 || X86 || (PPC && EXPERIMENTAL) || MICROBLAZE | 38 | depends on SUPERH || S390 || X86 || (PPC && EXPERIMENTAL) || MICROBLAZE || ARM |
39 | default n | 39 | default n |
40 | ---help--- | 40 | ---help--- |
41 | This options activates profiling for the entire kernel. | 41 | This options activates profiling for the entire kernel. |
@@ -46,4 +46,10 @@ config GCOV_PROFILE_ALL | |||
46 | larger and run slower. Also be sure to exclude files from profiling | 46 | larger and run slower. Also be sure to exclude files from profiling |
47 | which are not linked to the kernel image to prevent linker errors. | 47 | which are not linked to the kernel image to prevent linker errors. |
48 | 48 | ||
49 | config GCOV_CTORS | ||
50 | string | ||
51 | depends on CONSTRUCTORS | ||
52 | default ".init_array" if ARM && AEABI | ||
53 | default ".ctors" | ||
54 | |||
49 | endmenu | 55 | endmenu |
diff --git a/kernel/gcov/gcc_3_4.c b/kernel/gcov/gcc_3_4.c index ae5bb426003..d753d1152b7 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) | |||
297 | } | 297 | } |
298 | 298 | ||
299 | /* Mapping of logical record number to actual file content. */ | 299 | /* Mapping of logical record number to actual file content. */ |
300 | #define RECORD_FILE_MAGIC 0 | 300 | #define RECORD_FILE_MAGIC 0 |
301 | #define RECORD_GCOV_VERSION 1 | 301 | #define RECORD_GCOV_VERSION 1 |
302 | #define RECORD_TIME_STAMP 2 | 302 | #define RECORD_TIME_STAMP 2 |
303 | #define RECORD_FUNCTION_TAG 3 | 303 | #define RECORD_FUNCTION_TAG 3 |
304 | #define RECORD_FUNCTON_TAG_LEN 4 | 304 | #define RECORD_FUNCTON_TAG_LEN 4 |
305 | #define RECORD_FUNCTION_IDENT 5 | 305 | #define RECORD_FUNCTION_IDENT 5 |
306 | #define RECORD_FUNCTION_CHECK 6 | 306 | #define RECORD_FUNCTION_CHECK_LINE 6 |
307 | #define RECORD_COUNT_TAG 7 | 307 | #define RECORD_FUNCTION_CHECK_CFG 7 |
308 | #define RECORD_COUNT_LEN 8 | 308 | #define RECORD_FUNCTION_NAME_LEN 8 |
309 | #define RECORD_COUNT 9 | 309 | #define RECORD_FUNCTION_NAME 9 |
310 | #define RECORD_COUNT_TAG 10 | ||
311 | #define RECORD_COUNT_LEN 11 | ||
312 | #define RECORD_COUNT 12 | ||
313 | |||
314 | /* Return length of string encoded in GCOV format. */ | ||
315 | static size_t | ||
316 | sizeof_str(const char *str) | ||
317 | { | ||
318 | size_t len; | ||
319 | len = (str) ? strlen(str) : 0; | ||
320 | if (len == 0) | ||
321 | return 1; | ||
322 | return 1 + ((len + 4) >> 2); | ||
323 | } | ||
310 | 324 | ||
311 | /** | 325 | /** |
312 | * gcov_iter_next - advance file iterator to next logical record | 326 | * gcov_iter_next - advance file iterator to next logical record |
@@ -323,6 +337,9 @@ int gcov_iter_next(struct gcov_iterator *iter) | |||
323 | case RECORD_FUNCTON_TAG_LEN: | 337 | case RECORD_FUNCTON_TAG_LEN: |
324 | case RECORD_FUNCTION_IDENT: | 338 | case RECORD_FUNCTION_IDENT: |
325 | case RECORD_COUNT_TAG: | 339 | case RECORD_COUNT_TAG: |
340 | case RECORD_FUNCTION_CHECK_LINE: | ||
341 | case RECORD_FUNCTION_CHECK_CFG: | ||
342 | case RECORD_FUNCTION_NAME_LEN: | ||
326 | /* Advance to next record */ | 343 | /* Advance to next record */ |
327 | iter->record++; | 344 | iter->record++; |
328 | break; | 345 | break; |
@@ -332,7 +349,7 @@ int gcov_iter_next(struct gcov_iterator *iter) | |||
332 | /* fall through */ | 349 | /* fall through */ |
333 | case RECORD_COUNT_LEN: | 350 | case RECORD_COUNT_LEN: |
334 | if (iter->count < get_func(iter)->n_ctrs[iter->type]) { | 351 | if (iter->count < get_func(iter)->n_ctrs[iter->type]) { |
335 | iter->record = 9; | 352 | iter->record = 12; |
336 | break; | 353 | break; |
337 | } | 354 | } |
338 | /* Advance to next counter type */ | 355 | /* Advance to next counter type */ |
@@ -340,9 +357,9 @@ int gcov_iter_next(struct gcov_iterator *iter) | |||
340 | iter->count = 0; | 357 | iter->count = 0; |
341 | iter->type++; | 358 | iter->type++; |
342 | /* fall through */ | 359 | /* fall through */ |
343 | case RECORD_FUNCTION_CHECK: | 360 | case RECORD_FUNCTION_NAME: |
344 | if (iter->type < iter->num_types) { | 361 | if (iter->type < iter->num_types) { |
345 | iter->record = 7; | 362 | iter->record = 10; |
346 | break; | 363 | break; |
347 | } | 364 | } |
348 | /* Advance to next function */ | 365 | /* Advance to next function */ |
@@ -395,6 +412,34 @@ static int seq_write_gcov_u64(struct seq_file *seq, u64 v) | |||
395 | data[1] = (v >> 32); | 412 | data[1] = (v >> 32); |
396 | return seq_write(seq, data, sizeof(data)); | 413 | return seq_write(seq, data, sizeof(data)); |
397 | } | 414 | } |
415 | /** | ||
416 | * seq_write_gcov_str - write string in gcov format to seq_file | ||
417 | * @seq: seq_file handle | ||
418 | * @str: string to be stored | ||
419 | * | ||
420 | * Number format defined by gcc: numbers are recorded in the 32 bit | ||
421 | * unsigned binary form of the endianness of the machine generating the | ||
422 | * file. 64 bit numbers are stored as two 32 bit numbers, the low part | ||
423 | * first. | ||
424 | */ | ||
425 | static int seq_write_gcov_str(struct seq_file *seq, const char *str) | ||
426 | { | ||
427 | if (str) { | ||
428 | size_t len; | ||
429 | int str_off; | ||
430 | u32 data; | ||
431 | len = strlen(str); | ||
432 | for (str_off = 0; str_off < (sizeof_str(str) - 2) ; str_off++) { | ||
433 | memcpy(&data, (str + str_off * 4), 4); | ||
434 | seq_write(seq, &data, sizeof(data)); | ||
435 | } | ||
436 | data = 0; | ||
437 | memcpy(&data, (str + str_off * 4), (len - str_off * 4)); | ||
438 | return seq_write(seq, &data, sizeof(data)); | ||
439 | } else { | ||
440 | return 0; | ||
441 | } | ||
442 | } | ||
398 | 443 | ||
399 | /** | 444 | /** |
400 | * gcov_iter_write - write data for current pos to seq_file | 445 | * 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) | |||
421 | rc = seq_write_gcov_u32(seq, GCOV_TAG_FUNCTION); | 466 | rc = seq_write_gcov_u32(seq, GCOV_TAG_FUNCTION); |
422 | break; | 467 | break; |
423 | case RECORD_FUNCTON_TAG_LEN: | 468 | case RECORD_FUNCTON_TAG_LEN: |
424 | rc = seq_write_gcov_u32(seq, 2); | 469 | rc = seq_write_gcov_u32(seq, GCOV_TAG_FUNCTION_LENGTH + |
470 | (sizeof_str(get_func(iter)->name))); | ||
425 | break; | 471 | break; |
426 | case RECORD_FUNCTION_IDENT: | 472 | case RECORD_FUNCTION_IDENT: |
427 | rc = seq_write_gcov_u32(seq, get_func(iter)->ident); | 473 | rc = seq_write_gcov_u32(seq, get_func(iter)->ident); |
428 | break; | 474 | break; |
429 | case RECORD_FUNCTION_CHECK: | 475 | case RECORD_FUNCTION_CHECK_LINE: |
430 | rc = seq_write_gcov_u32(seq, get_func(iter)->checksum); | 476 | rc = seq_write_gcov_u32(seq, get_func(iter)->lineno_checksum); |
477 | break; | ||
478 | case RECORD_FUNCTION_CHECK_CFG: | ||
479 | rc = seq_write_gcov_u32(seq, get_func(iter)->cfg_checksum); | ||
480 | break; | ||
481 | case RECORD_FUNCTION_NAME_LEN: | ||
482 | rc = seq_write_gcov_u32(seq, | ||
483 | (sizeof_str(get_func(iter)->name) - 1)); | ||
484 | break; | ||
485 | case RECORD_FUNCTION_NAME: | ||
486 | rc = seq_write_gcov_str(seq, get_func(iter)->name); | ||
431 | break; | 487 | break; |
432 | case RECORD_COUNT_TAG: | 488 | case RECORD_COUNT_TAG: |
433 | rc = seq_write_gcov_u32(seq, | 489 | rc = seq_write_gcov_u32(seq, |
diff --git a/kernel/gcov/gcov.h b/kernel/gcov/gcov.h index 060073ebf7a..040c6980df0 100644 --- a/kernel/gcov/gcov.h +++ b/kernel/gcov/gcov.h | |||
@@ -21,9 +21,10 @@ | |||
21 | * gcc and need to be kept as close to the original definition as possible to | 21 | * gcc and need to be kept as close to the original definition as possible to |
22 | * remain compatible. | 22 | * remain compatible. |
23 | */ | 23 | */ |
24 | #define GCOV_COUNTERS 5 | 24 | #define GCOV_COUNTERS 10 |
25 | #define GCOV_DATA_MAGIC ((unsigned int) 0x67636461) | 25 | #define GCOV_DATA_MAGIC ((unsigned int) 0x67636461) |
26 | #define GCOV_TAG_FUNCTION ((unsigned int) 0x01000000) | 26 | #define GCOV_TAG_FUNCTION ((unsigned int) 0x01000000) |
27 | #define GCOV_TAG_FUNCTION_LENGTH 3 | ||
27 | #define GCOV_TAG_COUNTER_BASE ((unsigned int) 0x01a10000) | 28 | #define GCOV_TAG_COUNTER_BASE ((unsigned int) 0x01a10000) |
28 | #define GCOV_TAG_FOR_COUNTER(count) \ | 29 | #define GCOV_TAG_FOR_COUNTER(count) \ |
29 | (GCOV_TAG_COUNTER_BASE + ((unsigned int) (count) << 17)) | 30 | (GCOV_TAG_COUNTER_BASE + ((unsigned int) (count) << 17)) |
@@ -34,10 +35,38 @@ typedef long gcov_type; | |||
34 | typedef long long gcov_type; | 35 | typedef long long gcov_type; |
35 | #endif | 36 | #endif |
36 | 37 | ||
38 | /* | ||
39 | * Source module info. The data structure is used in both runtime and | ||
40 | * profile-use phase. | ||
41 | */ | ||
42 | struct gcov_module_info { | ||
43 | unsigned int ident; | ||
44 | /* | ||
45 | * This is overloaded to mean two things: | ||
46 | * (1) means FDO/LIPO in instrumented binary. | ||
47 | * (2) means IS_PRIMARY in persistent file or memory copy used in profile-use. | ||
48 | */ | ||
49 | unsigned int is_primary; | ||
50 | unsigned int is_exported; | ||
51 | unsigned int lang; | ||
52 | char *da_filename; | ||
53 | char *source_filename; | ||
54 | unsigned int num_quote_paths; | ||
55 | unsigned int num_bracket_paths; | ||
56 | unsigned int num_cpp_defines; | ||
57 | unsigned int num_cpp_includes; | ||
58 | unsigned int num_cl_args; | ||
59 | char *string_array[1]; | ||
60 | }; | ||
61 | |||
62 | |||
37 | /** | 63 | /** |
38 | * struct gcov_fn_info - profiling meta data per function | 64 | * struct gcov_fn_info - profiling meta data per function |
39 | * @ident: object file-unique function identifier | 65 | * @ident: object file-unique function identifier |
40 | * @checksum: function checksum | 66 | * @lineno_checksum: function lineno checksum |
67 | * @cfg_checksum: function cfg checksum | ||
68 | * @dc_offset: direct call offset | ||
69 | * @name: function name | ||
41 | * @n_ctrs: number of values per counter type belonging to this function | 70 | * @n_ctrs: number of values per counter type belonging to this function |
42 | * | 71 | * |
43 | * This data is generated by gcc during compilation and doesn't change | 72 | * This data is generated by gcc during compilation and doesn't change |
@@ -45,7 +74,10 @@ typedef long long gcov_type; | |||
45 | */ | 74 | */ |
46 | struct gcov_fn_info { | 75 | struct gcov_fn_info { |
47 | unsigned int ident; | 76 | unsigned int ident; |
48 | unsigned int checksum; | 77 | unsigned int lineno_checksum; |
78 | unsigned int cfg_checksum; | ||
79 | unsigned int dc_offset; | ||
80 | const char *name; | ||
49 | unsigned int n_ctrs[0]; | 81 | unsigned int n_ctrs[0]; |
50 | }; | 82 | }; |
51 | 83 | ||
@@ -67,9 +99,11 @@ struct gcov_ctr_info { | |||
67 | /** | 99 | /** |
68 | * struct gcov_info - profiling data per object file | 100 | * struct gcov_info - profiling data per object file |
69 | * @version: gcov version magic indicating the gcc version used for compilation | 101 | * @version: gcov version magic indicating the gcc version used for compilation |
102 | * @modinfo: additional module information | ||
70 | * @next: list head for a singly-linked list | 103 | * @next: list head for a singly-linked list |
71 | * @stamp: time stamp | 104 | * @stamp: time stamp |
72 | * @filename: name of the associated gcov data file | 105 | * @filename: name of the associated gcov data file |
106 | * @eof_pos: end position of profile data | ||
73 | * @n_functions: number of instrumented functions | 107 | * @n_functions: number of instrumented functions |
74 | * @functions: function data | 108 | * @functions: function data |
75 | * @ctr_mask: mask specifying which counter types are active | 109 | * @ctr_mask: mask specifying which counter types are active |
@@ -80,9 +114,11 @@ struct gcov_ctr_info { | |||
80 | */ | 114 | */ |
81 | struct gcov_info { | 115 | struct gcov_info { |
82 | unsigned int version; | 116 | unsigned int version; |
117 | struct gcov_module_info *mod_info; | ||
83 | struct gcov_info *next; | 118 | struct gcov_info *next; |
84 | unsigned int stamp; | 119 | unsigned int stamp; |
85 | const char *filename; | 120 | const char *filename; |
121 | unsigned int eof_pos; | ||
86 | unsigned int n_functions; | 122 | unsigned int n_functions; |
87 | const struct gcov_fn_info *functions; | 123 | const struct gcov_fn_info *functions; |
88 | unsigned int ctr_mask; | 124 | unsigned int ctr_mask; |