/* Copyright 2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef u32 gcov_unsigned_int; /* You will need to pass -DTARGET__GNUC__=blah when building */ #if (__GNUC__ >= 7) #define GCOV_COUNTERS 9 #else #if TARGET__GNUC__ >= 6 || (TARGET__GNUC__ >= 5 && TARGET__GNUC_MINOR__ >= 1) #define GCOV_COUNTERS 10 #else #if TARGET__GNUC__ >= 4 && TARGET__GNUC_MINOR__ >= 9 #define GCOV_COUNTERS 9 #else #define GCOV_COUNTERS 8 #endif /* GCC 4.9 */ #endif /* GCC 5.1 */ #endif /* GCC 7 */ typedef u64 gcov_type; struct gcov_info { gcov_unsigned_int version; u32 _padding; struct gcov_info *next; gcov_unsigned_int stamp; u32 _padding2; const char *filename; u64 merge[GCOV_COUNTERS]; unsigned int n_functions; u32 _padding3; struct gcov_fn_info **functions; }; struct gcov_ctr_info { gcov_unsigned_int num; u32 _padding; gcov_type *values; }__attribute__((packed)); struct gcov_fn_info { const struct gcov_info *key; unsigned int ident; unsigned int lineno_checksum; unsigned int cfg_checksum; u32 _padding; // struct gcov_ctr_info ctrs[0]; } __attribute__((packed)); /* We have a list of all gcov info set up at startup */ struct gcov_info *gcov_info_list; #define SKIBOOT_OFFSET 0x30000000 /* Endian of the machine producing the gcda. Which mean BE. * because skiboot is BE. * If skiboot is ever LE, go have fun. */ static size_t write_u32(int fd, u32 _v) { u32 v = htobe32(_v); return write(fd, &v, sizeof(v)); } static size_t write_u64(int fd, u64 v) { u32 b[2]; b[0] = htobe32(v & 0xffffffffUL); b[1] = htobe32(v >> 32); write(fd, &b[0], sizeof(u32)); write(fd, &b[1], sizeof(u32)); return sizeof(u64); } #define GCOV_DATA_MAGIC ((unsigned int) 0x67636461) #define GCOV_TAG_FUNCTION ((unsigned int) 0x01000000) #define GCOV_TAG_COUNTER_BASE ((unsigned int) 0x01a10000) #define GCOV_TAG_FOR_COUNTER(count) \ (GCOV_TAG_COUNTER_BASE + ((unsigned int) (count) << 17)) // gcc 4.7/4.8 specific #define GCOV_TAG_FUNCTION_LENGTH 3 size_t skiboot_dump_size = 0x240000; static inline const char* SKIBOOT_ADDR(const char* addr, const void* p) { const char* r= (addr + (be64toh((const u64)p) - SKIBOOT_OFFSET)); assert(r < (addr + skiboot_dump_size)); return r; } static int counter_active(struct gcov_info *info, unsigned int type) { return info->merge[type] ? 1 : 0; } static void write_gcda(char *addr, struct gcov_info* gi) { const char* filename = SKIBOOT_ADDR(addr, gi->filename); int fd; u32 fn; struct gcov_fn_info *fn_info; struct gcov_fn_info **functions; struct gcov_ctr_info *ctr_info; u32 ctr; u32 cv; printf("Writing %s\n", filename); fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR); if (fd < 0) { fprintf(stderr, "Error opening file %s: %d %s\n", filename, errno, strerror(errno)); exit(EXIT_FAILURE); } write_u32(fd, GCOV_DATA_MAGIC); write_u32(fd, be32toh(gi->version)); write_u32(fd, be32toh(gi->stamp)); printf("version: %x\tstamp: %d\n", be32toh(gi->version), be32toh(gi->stamp)); printf("nfunctions: %d \n", be32toh(gi->n_functions)); for(fn = 0; fn < be32toh(gi->n_functions); fn++) { functions = (struct gcov_fn_info**) SKIBOOT_ADDR(addr, gi->functions); fn_info = (struct gcov_fn_info*) SKIBOOT_ADDR(addr, functions[fn]); printf("function: %p\n", (void*)be64toh((u64)functions[fn])); write_u32(fd, GCOV_TAG_FUNCTION); write_u32(fd, GCOV_TAG_FUNCTION_LENGTH); write_u32(fd, be32toh(fn_info->ident)); write_u32(fd, be32toh(fn_info->lineno_checksum)); write_u32(fd, be32toh(fn_info->cfg_checksum)); ctr_info = (struct gcov_ctr_info*) ((char*)fn_info + sizeof(struct gcov_fn_info)); for(ctr = 0; ctr < GCOV_COUNTERS; ctr++) { if (!counter_active(gi, ctr)) continue; write_u32(fd, (GCOV_TAG_FOR_COUNTER(ctr))); write_u32(fd, be32toh(ctr_info->num)*2); printf(" ctr %d gcov_ctr_info->num %u\n", ctr, be32toh(ctr_info->num)); for(cv = 0; cv < be32toh(ctr_info->num); cv++) { gcov_type *ctrv = (gcov_type *) SKIBOOT_ADDR(addr, ctr_info->values); //printf("%lx\n", be64toh(ctrv[cv])); write_u64(fd, be64toh(ctrv[cv])); } ctr_info++; } } close(fd); } int main(int argc, char *argv[]) { int r; int fd; struct stat sb; char *addr; u64 gcov_list_addr; printf("sizes: %zu %zu %zu %zu\n", sizeof(gcov_unsigned_int), sizeof(struct gcov_ctr_info), sizeof(struct gcov_fn_info), sizeof(struct gcov_info)); printf("TARGET GNUC: %d.%d\n", TARGET__GNUC__, TARGET__GNUC_MINOR__); printf("GCOV_COUNTERS: %d\n", GCOV_COUNTERS); if (argc < 3) { fprintf(stderr, "Usage:\n" "\t%s skiboot.dump gcov_offset\n\n", argv[0]); return -1; } /* argv[1] = skiboot.dump */ fd = open(argv[1], O_RDONLY); if (fd < 0) { fprintf(stderr, "Cannot open dump: %s (error %d %s)\n", argv[1], errno, strerror(errno)); exit(-1); } r = fstat(fd, &sb); if (r < 0) { fprintf(stderr, "Cannot stat dump, %d %s\n", errno, strerror(errno)); exit(-1); } addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); assert(addr != NULL); skiboot_dump_size = sb.st_size; printf("Skiboot memory dump %p - %p\n", (void*)SKIBOOT_OFFSET, (void*)SKIBOOT_OFFSET+sb.st_size); gcov_list_addr = strtoll(argv[2], NULL, 0); gcov_list_addr = (u64)(addr + (gcov_list_addr - SKIBOOT_OFFSET)); gcov_list_addr = be64toh(*(u64*)gcov_list_addr); printf("Skiboot gcov_info_list at %p\n", (void*)gcov_list_addr); do { gcov_info_list = (struct gcov_info *)(addr + (gcov_list_addr - SKIBOOT_OFFSET)); write_gcda(addr, gcov_info_list); gcov_list_addr = be64toh((u64)gcov_info_list->next); } while(gcov_list_addr); munmap(addr, sb.st_size); close(fd); return 0; }