260 lines
6.6 KiB
C
260 lines
6.6 KiB
C
/* 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;
|
|
}
|