| #include <stdio.h> |
| #include <stdlib.h> |
| #include <inttypes.h> |
| #include "gtrace.h" |
| |
| // A buffer of zeros |
| static char zeros[Gtrace::kGtraceEntriesPerBlock * sizeof(Gtrace::trace_entry)]; |
| |
| Gtrace::Gtrace() { |
| gtrace_file_ = NULL; |
| ftrace_ = NULL; |
| fnames_ = NULL; |
| start_sec_ = 0; |
| pdate_ = 0; |
| ptime_ = 0; |
| num_entries_ = 0; |
| blockno_ = 1; |
| current_pid_ = 0; |
| } |
| |
| Gtrace::~Gtrace() { |
| if (ftrace_) { |
| // Extend the trace file to a multiple of 8k. Otherwise gtracepost64 |
| // complains. |
| long pos = ftell(ftrace_); |
| long pos_end = (pos + 0x1fff) & ~0x1fff; |
| if (pos_end > pos) { |
| char ch = 0; |
| fseek(ftrace_, pos_end - 1, SEEK_SET); |
| fwrite(&ch, 1, 1, ftrace_); |
| } |
| fclose(ftrace_); |
| } |
| if (fnames_) |
| fclose(fnames_); |
| } |
| |
| void Gtrace::Open(const char *gtrace_file, uint32_t pdate, uint32_t ptime) |
| { |
| ftrace_ = fopen(gtrace_file, "w"); |
| if (ftrace_ == NULL) { |
| perror(gtrace_file); |
| exit(1); |
| } |
| gtrace_file_ = gtrace_file; |
| |
| pdate_ = pdate; |
| ptime_ = ptime; |
| sprintf(gname_file_, "gname_%x_%06x.txt", pdate, ptime); |
| fnames_ = fopen(gname_file_, "w"); |
| if (fnames_ == NULL) { |
| perror(gname_file_); |
| exit(1); |
| } |
| fprintf(fnames_, "# File# Proc# Line# Name\n"); |
| } |
| |
| void Gtrace::WriteFirstHeader(uint32_t start_sec, uint32_t pid) |
| { |
| first_header fh; |
| current_pid_ = pid; |
| start_sec_ = start_sec; |
| FillFirstHeader(start_sec, pid, &fh); |
| fwrite(&fh, sizeof(fh), 1, ftrace_); |
| num_entries_ = 8; |
| } |
| |
| void Gtrace::FillFirstHeader(uint32_t start_sec, uint32_t pid, |
| first_header *fh) { |
| int cpu = 0; |
| int max_files = 16; |
| int max_procedures = 12; |
| |
| fh->common.blockno = 0; |
| fh->common.entry_width = 8; |
| fh->common.block_tic = kBaseTic; |
| fh->common.block_time = start_sec; |
| //fh->common.usec_cpu = (start_usec << 8) | (cpu & 0xff); |
| fh->common.usec_cpu = cpu & 0xff; |
| fh->common.pid = pid; |
| fh->common.bug_count = 0; |
| fh->common.zero_count = 0; |
| |
| fh->tic = kBaseTic + 1; |
| fh->one = 1; |
| fh->tics_per_second = kTicsPerSecond; |
| fh->trace_time = start_sec; |
| fh->version = 5; |
| fh->file_proc = (max_files << 8) | max_procedures; |
| fh->pdate = pdate_; |
| fh->ptime = ptime_; |
| } |
| |
| void Gtrace::WriteBlockHeader(uint32_t cycle, uint32_t pid) |
| { |
| int cpu = 0; |
| block_header bh; |
| |
| bh.blockno = blockno_++; |
| bh.entry_width = 8; |
| bh.block_tic = cycle + kBaseTic; |
| bh.block_time = start_sec_ + cycle / kTicsPerSecond; |
| //bh.usec_cpu = (start_usec << 8) | (cpu & 0xff); |
| bh.usec_cpu = cpu & 0xff; |
| bh.pid = pid; |
| bh.bug_count = 0; |
| bh.zero_count = 0; |
| fwrite(&bh, sizeof(bh), 1, ftrace_); |
| } |
| |
| void Gtrace::AddGtraceRecord(int filenum, int procnum, uint32_t cycle, uint32_t pid, |
| int is_exit) |
| { |
| trace_entry entry; |
| |
| if (current_pid_ != pid) { |
| current_pid_ = pid; |
| |
| // We are switching to a new process id, so pad the current block |
| // with zeros. |
| int num_zeros = (kGtraceEntriesPerBlock - num_entries_) * sizeof(entry); |
| fwrite(zeros, num_zeros, 1, ftrace_); |
| WriteBlockHeader(cycle, pid); |
| num_entries_ = 4; |
| } |
| |
| // If the current block is full, write out a new block header |
| if (num_entries_ == kGtraceEntriesPerBlock) { |
| WriteBlockHeader(cycle, pid); |
| num_entries_ = 4; |
| } |
| |
| entry.cycle = cycle + kBaseTic; |
| entry.event = (filenum << 13) | (procnum << 1) | is_exit; |
| fwrite(&entry, sizeof(entry), 1, ftrace_); |
| num_entries_ += 1; |
| } |
| |
| void Gtrace::AddProcEntry(int filenum, int procnum, uint32_t cycle, uint32_t pid) |
| { |
| AddGtraceRecord(filenum, procnum, cycle, pid, 0); |
| } |
| |
| void Gtrace::AddProcExit(int filenum, int procnum, uint32_t cycle, uint32_t pid) |
| { |
| AddGtraceRecord(filenum, procnum, cycle, pid, 1); |
| } |
| |
| void Gtrace::AddProcedure(int filenum, int procnum, const char *proc_name) |
| { |
| fprintf(fnames_, "%d %d %d %s\n", filenum, procnum, procnum, proc_name); |
| } |