blob: e5bcf165342f459a0bde6a48a27210910991e64d [file] [log] [blame]
/* 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 <ccan/short_types/short_types.h>
#include <endian.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/mman.h>
#include <assert.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
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;
}