Index: usr.sbin/Makefile.aarch64 =================================================================== --- usr.sbin/Makefile.aarch64 +++ usr.sbin/Makefile.aarch64 @@ -4,3 +4,4 @@ SUBDIR+= acpi .endif SUBDIR+= ofwdump +SUBDIR+= hwt Index: usr.sbin/hwt/Makefile =================================================================== --- /dev/null +++ usr.sbin/hwt/Makefile @@ -0,0 +1,17 @@ +# $FreeBSD$ + +PROG_CXX= hwt +MAN= + +CFLAGS+= -I${SRCTOP}/lib/libpmcstat + +LIBADD= elf opencsd pmcstat + +SRCS= hwt.c \ + hwt_coresight.c \ + hwt_elf.c \ + hwt_process.c \ + hwt_record.c \ + libpmcstat_stubs.c + +.include Index: usr.sbin/hwt/hwt.c =================================================================== --- /dev/null +++ usr.sbin/hwt/hwt.c @@ -0,0 +1,286 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "libpmcstat_stubs.h" +#include + +#include "hwtvar.h" +#include "hwt_coresight.h" + +#define PARENTSOCKET 0 +#define CHILDSOCKET 1 +#define NSOCKPAIRFD 2 + +static struct trace_context tcs; +static int ncpu; + +void +hwt_sleep(void) +{ + struct timespec time_to_sleep; + + time_to_sleep.tv_sec = 0; + time_to_sleep.tv_nsec = 10000000; /* 10 ms */ + + nanosleep(&time_to_sleep, &time_to_sleep); +} + +void +hwt_procexit(pid_t pid, int exit_status __unused) +{ + struct trace_context *tc; + + tc = &tcs; + + if (tc->pid == pid) + tc->terminate = 1; +} + +static int +hwt_ctx_alloc(int fd) +{ + struct trace_context *tc; + struct hwt_alloc al; + int error; + + tc = &tcs; + + al.pid = tc->pid; + al.bufsize = tc->bufsize; + al.backend_name = "coresight"; + + error = ioctl(fd, HWT_IOC_ALLOC, &al); + if (error != 0) + return (error); + + return (0); +} + +static int +hwt_map_memory(struct trace_context *tc, int tid) +{ + char filename[32]; + + sprintf(filename, "/dev/hwt_%d_%d", tc->pid, tid); + + tc->thr_fd = open(filename, O_RDONLY); + if (tc->thr_fd < 0) { + printf("Can't open %s\n", filename); + return (-1); + } + + tc->base = mmap(NULL, tc->bufsize, PROT_READ, MAP_SHARED, tc->thr_fd, + 0); + if (tc->base == MAP_FAILED) { + printf("mmap failed: err %d\n", errno); + return (-1); + } + + printf("%s: tc->base %#p\n", __func__, tc->base); + + return (0); +} + +size_t +hwt_get_offs(struct trace_context *tc, size_t *offs) +{ + struct hwt_bufptr_get bget; + vm_offset_t curpage_offset; + int curpage; + int error; + int ptr; + + bget.pid = tc->pid; + bget.ptr = &ptr; + bget.curpage = &curpage; + bget.curpage_offset = &curpage_offset; + error = ioctl(tc->thr_fd, HWT_IOC_BUFPTR_GET, &bget); + if (error) + return (error); + + printf("curpage %d curpage_offset %ld\n", curpage, curpage_offset); + + *offs = curpage * PAGE_SIZE + curpage_offset; + + return (0); +} + +static int +hwt_get_records(uint32_t *nrec) +{ + struct trace_context *tc; + int tot_records; + int nrecords; + int error; + + tc = &tcs; + + tot_records = 0; + + error = hwt_record_fetch(tc, &nrecords); + if (error) + return (error); + + tot_records += nrecords; + + *nrec = tot_records; + + return (0); +} + +int +main(int argc __unused, char **argv, char **env) +{ + struct hwt_record_user_entry *entry; + struct pmcstat_process *pp; + struct trace_context *tc; + struct hwt_start s; + uint32_t tot_rec; + uint32_t nrec; + uint32_t nlibs; + char **cmd; + int error; + int fd; + int sockpair[NSOCKPAIRFD]; + int pid; + size_t bufsize; + + cmd = argv + 1; + + tc = &tcs; + + error = hwt_elf_count_libs(*cmd, &nlibs); + if (error != 0) { + printf("could not count libs\n"); + return (error); + } + + nlibs += 1; /* add binary itself. */ + + ncpu = sysconf(_SC_NPROCESSORS_CONF); + + printf("cmd is %s, nlibs %d\n", *cmd, nlibs); + + fd = open("/dev/hwt", O_RDWR); + if (fd < 0) { + printf("Can't open /dev/hwt\n"); + return (-1); + } + + error = hwt_process_create(sockpair, cmd, env, &pid); + if (error != 0) + return (error); + + printf("%s: process pid %d created\n", __func__, pid); + + pp = hwt_process_alloc(); + pp->pp_pid = pid; + pp->pp_isactive = 1; + + bufsize = 16 * 1024 * 1024; + + tc->pp = pp; + tc->pid = pid; + tc->fd = fd; + + tc->bufsize = bufsize; + + error = hwt_ctx_alloc(fd); + if (error) { + printf("%s: failed to alloc ctx, pid %d error %d\n", __func__, + tc->pid, error); + + while (1); + + return (error); + } + + error = hwt_get_records(&nrec); + if (error != 0) + return (error); + + if (nrec != 1) + return (error); + + entry = &tc->records[0]; + + error = hwt_map_memory(tc, entry->tid); + if (error != 0) { + printf("can't map memory"); + return (error); + } + + printf("starting tracing\n"); + + s.pid = tc->pid; + error = ioctl(fd, HWT_IOC_START, &s); + if (error) { + printf("%s: failed to start tracing, error %d\n", __func__, + error); + return (error); + } + + error = hwt_process_start(sockpair); + if (error != 0) + return (error); + + printf("nlibs %d\n", nlibs); + + tot_rec = 0; + + do { + error = hwt_get_records(&nrec); + if (error != 0) + return (error); + tot_rec += nrec; + } while (tot_rec < nlibs); + + hwt_coresight_process(tc); + + close(fd); + + return (0); +} Index: usr.sbin/hwt/hwt_coresight.h =================================================================== --- /dev/null +++ usr.sbin/hwt/hwt_coresight.h @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 2023 Ruslan Bukin + * All rights reserved. + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _HWT_CORESIGHT_H_ +#define _HWT_CORESIGHT_H_ + +int hwt_coresight_init(struct trace_context *tc); +int hwt_coresight_process(struct trace_context *tcs); + +#endif /* !_HWT_CORESIGHT_H_ */ Index: usr.sbin/hwt/hwt_coresight.c =================================================================== --- /dev/null +++ usr.sbin/hwt/hwt_coresight.c @@ -0,0 +1,616 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* ARM CoreSight tracing unit. */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "hwtvar.h" +#include "hwt_coresight.h" + +#include "libpmcstat_stubs.h" +#include + +#define PMCTRACE_CS_DEBUG +#undef PMCTRACE_CS_DEBUG + +#ifdef PMCTRACE_CS_DEBUG +#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__) +#else +#define dprintf(fmt, ...) +#endif + +static dcd_tree_handle_t dcdtree_handle; +static int cs_flags = 0; +#define FLAG_FORMAT (1 << 0) +#define FLAG_FRAME_RAW_UNPACKED (1 << 1) +#define FLAG_FRAME_RAW_PACKED (1 << 2) +#define FLAG_CALLBACK_MEM_ACC (1 << 3) + +#define PACKET_STR_LEN 1024 +static char packet_str[PACKET_STR_LEN]; + +static ocsd_err_t +attach_raw_printers(dcd_tree_handle_t dcd_tree_h) +{ + ocsd_err_t err; + int flags; + + flags = 0; + err = OCSD_OK; + + if (cs_flags & FLAG_FRAME_RAW_UNPACKED) + flags |= OCSD_DFRMTR_UNPACKED_RAW_OUT; + + if (cs_flags & FLAG_FRAME_RAW_PACKED) + flags |= OCSD_DFRMTR_PACKED_RAW_OUT; + + if (flags) + err = ocsd_dt_set_raw_frame_printer(dcd_tree_h, flags); + + return err; +} + +static int +print_data_array(const uint8_t *p_array, const int array_size, + char *p_buffer, int buf_size) +{ + int bytes_processed; + int chars_printed; + + chars_printed = 0; + p_buffer[0] = 0; + + if (buf_size > 9) { + strcat(p_buffer, "[ "); + chars_printed += 2; + + for (bytes_processed = 0; bytes_processed < array_size; + bytes_processed++) { + sprintf(p_buffer + chars_printed, "0x%02X ", + p_array[bytes_processed]); + chars_printed += 5; + if ((chars_printed + 5) > buf_size) + break; + } + + strcat(p_buffer, "];"); + chars_printed += 2; + } else if (buf_size >= 4) { + sprintf(p_buffer, "[];"); + chars_printed += 3; + } + + return (chars_printed); +} + +static void +packet_monitor(void *context __unused, + const ocsd_datapath_op_t op, + const ocsd_trc_index_t index_sop, + const void *p_packet_in, + const uint32_t size, + const uint8_t *p_data) +{ + int offset; + + offset = 0; + + switch (op) { + case OCSD_OP_DATA: + sprintf(packet_str, "Idx:%" OCSD_TRC_IDX_STR ";", index_sop); + offset = strlen(packet_str); + offset += print_data_array(p_data, size, packet_str + offset, + PACKET_STR_LEN - offset); + + /* + * Got a packet -- convert to string and use the libraries' + * message output to print to file and stdoout + */ + + if (ocsd_pkt_str(OCSD_PROTOCOL_ETMV4I, p_packet_in, + packet_str + offset, PACKET_STR_LEN - offset) == OCSD_OK) { + /* add in */ + if (strlen(packet_str) == PACKET_STR_LEN - 1)/*maxlen*/ + packet_str[PACKET_STR_LEN - 2] = '\n'; + else + strcat(packet_str,"\n"); + + /* print it using the library output logger. */ + ocsd_def_errlog_msgout(packet_str); + } + break; + + case OCSD_OP_EOT: + sprintf(packet_str,"**** END OF TRACE ****\n"); + ocsd_def_errlog_msgout(packet_str); + break; + default: + printf("%s: unknown op %d\n", __func__, op); + break; + } +} + +#if 0 +static uint32_t +cs_decoder__mem_access(const void *context __unused, + const ocsd_vaddr_t address __unused, + const ocsd_mem_space_acc_t mem_space __unused, + const uint32_t req_size __unused, uint8_t *buffer __unused) +{ + + /* TODO */ + + return (0); +} +#endif + +static ocsd_err_t +create_test_memory_acc(dcd_tree_handle_t handle, struct trace_context *tc) +{ + ocsd_vaddr_t address; + uint8_t *p_mem_buffer; + uint32_t mem_length; + int ret; + + dprintf("%s\n", __func__); + +#if 0 + if (cs_flags & FLAG_CALLBACK_MEM_ACC) + ret = ocsd_dt_add_callback_mem_acc(handle, base + start, + base + end - 1, OCSD_MEM_SPACE_ANY, + cs_decoder__mem_access, NULL); + else +#endif + { + address = (ocsd_vaddr_t)tc->base; + + uint64_t *t; + t = (uint64_t *)tc->base; + printf("%lx %lx %lx %lx\n", t[0], t[1], t[2], t[3]); + + p_mem_buffer = (uint8_t *)tc->base; + mem_length = tc->bufsize; + + ret = ocsd_dt_add_buffer_mem_acc(handle, address, + OCSD_MEM_SPACE_ANY, p_mem_buffer, mem_length); + } + + if (ret != OCSD_OK) + printf("%s: can't create memory accessor: ret %d\n", + __func__, ret); + + return (ret); +} + +static ocsd_err_t +create_generic_decoder(dcd_tree_handle_t handle, const char *p_name, + const void *p_cfg, const void *p_context __unused, + struct trace_context *tc) +{ + ocsd_err_t ret; + uint8_t CSID; + + CSID = 0; + + dprintf("%s\n", __func__); + + ret = ocsd_dt_create_decoder(handle, p_name, + OCSD_CREATE_FLG_FULL_DECODER, p_cfg, &CSID); + if (ret != OCSD_OK) + return (-1); + + printf("%s: CSID %d\n", __func__, CSID); + + if (cs_flags & FLAG_FORMAT) { + ret = ocsd_dt_attach_packet_callback(handle, CSID, + OCSD_C_API_CB_PKT_MON, packet_monitor, p_context); + if (ret != OCSD_OK) + return (-1); + } + + /* attach a memory accessor */ + ret = create_test_memory_acc(handle, tc); + if (ret != OCSD_OK) + ocsd_dt_remove_decoder(handle, CSID); + + return (ret); +} + +static ocsd_err_t +create_decoder_etmv4(dcd_tree_handle_t dcd_tree_h, struct trace_context *tc) +{ + ocsd_etmv4_cfg trace_config; + ocsd_err_t ret; + + trace_config.arch_ver = ARCH_V8; + trace_config.core_prof = profile_CortexA; + + trace_config.reg_configr = 0x00001fc6; + trace_config.reg_configr = 0x000000C1; + trace_config.reg_traceidr = 0x00000001; /* Trace ID */ + + trace_config.reg_idr0 = 0x28000ea1; + trace_config.reg_idr1 = 0x4100f424; + trace_config.reg_idr2 = 0x20001088; + trace_config.reg_idr8 = 0x0; + trace_config.reg_idr9 = 0x0; + trace_config.reg_idr10 = 0x0; + trace_config.reg_idr11 = 0x0; + trace_config.reg_idr12 = 0x0; + trace_config.reg_idr13 = 0x0; + + /* Instruction decoder. */ + ret = create_generic_decoder(dcd_tree_h, OCSD_BUILTIN_DCD_ETMV4I, + (void *)&trace_config, 0, tc); + + return (ret); +} + +static int +cs_process_chunk(struct trace_context *tc, size_t start, size_t end) +{ + uint32_t bytes_done; + uint8_t *p_block; + uint32_t bytes_this_time; + int block_index; + size_t block_size; + int dp_ret; + int ret; + + dprintf("%s: tc->base %#p\n", __func__, tc->base); + + bytes_this_time = 0; + block_index = start; + bytes_done = 0; + block_size = end - start; + p_block = (uint8_t *)((uintptr_t)tc->base + start); + + ret = OCSD_OK; + dp_ret = OCSD_RESP_CONT; + + while (bytes_done < (uint32_t)block_size && (ret == OCSD_OK)) { + + if (OCSD_DATA_RESP_IS_CONT(dp_ret)) { + dprintf("process data, block_size %ld, bytes_done %d\n", + block_size, bytes_done); + dp_ret = ocsd_dt_process_data(dcdtree_handle, + OCSD_OP_DATA, + block_index + bytes_done, + block_size - bytes_done, + ((uint8_t *)p_block) + bytes_done, + &bytes_this_time); + bytes_done += bytes_this_time; + dprintf("BYTES DONE %d\n", bytes_done); + if (OCSD_DATA_RESP_IS_WAIT(dp_ret)) { +printf("wait"); + exit(12); + } + + } else if (OCSD_DATA_RESP_IS_WAIT(dp_ret)) { +printf("WAIT"); +exit(5); + dp_ret = ocsd_dt_process_data(dcdtree_handle, + OCSD_OP_FLUSH, 0, 0, NULL, NULL); + } else { +printf("FATAL"); +exit(6); + ret = OCSD_ERR_DATA_DECODE_FATAL; + } + } + + //ocsd_dt_process_data(dcdtree_handle, OCSD_OP_EOT, 0, 0, NULL, NULL); + + return (0); +} + +struct pmcstat_pcmap * +pmcstat_process_find_map(struct pmcstat_process *p, uintfptr_t pc) +{ + struct pmcstat_pcmap *ppm; + + TAILQ_FOREACH(ppm, &p->pp_map, ppm_next) { + if (pc >= ppm->ppm_lowpc && pc < ppm->ppm_highpc) + return (ppm); + if (pc < ppm->ppm_lowpc) + return (NULL); + } + + return (NULL); +} + +static struct pmcstat_symbol * +symbol_lookup(const struct trace_context *tc, uint64_t ip, + struct pmcstat_image **img, uint64_t *newpc0) +{ + struct pmcstat_image *image; + struct pmcstat_symbol *sym; + struct pmcstat_pcmap *map; + uint64_t newpc; + + map = pmcstat_process_find_map(tc->pp, ip); + if (map != NULL) { + image = map->ppm_image; + newpc = ip - ((unsigned long)map->ppm_lowpc + + (image->pi_vaddr - image->pi_start)); + sym = pmcstat_symbol_search(image, newpc); /* Could be NULL. */ + newpc += image->pi_vaddr; + + *img = image; + *newpc0 = newpc; + + return (sym); + } else + *img = NULL; + + return (NULL); +} + + +static ocsd_datapath_resp_t +gen_trace_elem_print_lookup(const void *p_context, + const ocsd_trc_index_t index_sop __unused, + const uint8_t trc_chan_id __unused, + const ocsd_generic_trace_elem *elem) +{ + const struct trace_context *tc; + struct pmcstat_image *image; + ocsd_datapath_resp_t resp; + struct pmcstat_symbol *sym; + unsigned long offset; + uint64_t newpc; + uint64_t ip; + + tc = (const struct trace_context *)p_context; + + resp = OCSD_RESP_CONT; + +#if 0 + dprintf("%s: Idx:%d ELEM TYPE %d, st_addr %lx, en_addr %lx\n", + __func__, index_sop, elem->elem_type, + elem->st_addr, elem->en_addr); +#endif + +#if 0 + if (elem->st_addr == -1) + return (resp); +#endif + + if (elem->st_addr == 0) + return (resp); + ip = elem->st_addr; + + sym = symbol_lookup(tc, ip, &image, &newpc); + + static const char *ARMv8Excep[] = { + "PE Reset", "Debug Halt", "Call", "Trap", + "System Error", "Reserved", "Inst Debug", "Data Debug", + "Reserved", "Reserved", "Alignment", "Inst Fault", + "Data Fault", "Reserved", "IRQ", "FIQ" + }; + + switch (elem->elem_type) { + case OCSD_GEN_TRC_ELEM_UNKNOWN: + printf("Unknown packet.\n"); + return (resp); + case OCSD_GEN_TRC_ELEM_NO_SYNC: + printf("No sync.\n"); + return (resp); + case OCSD_GEN_TRC_ELEM_TRACE_ON: + printf("Trace on.\n"); + return (resp); + case OCSD_GEN_TRC_ELEM_EO_TRACE: + printf("End of Trace.\n"); + return (resp); + case OCSD_GEN_TRC_ELEM_PE_CONTEXT: + break; + case OCSD_GEN_TRC_ELEM_INSTR_RANGE: + case OCSD_GEN_TRC_ELEM_I_RANGE_NOPATH: + return (resp); + case OCSD_GEN_TRC_ELEM_ADDR_NACC: + break; + case OCSD_GEN_TRC_ELEM_ADDR_UNKNOWN: + return (resp); + case OCSD_GEN_TRC_ELEM_EXCEPTION: + printf("Exception #%d (%s)\n", elem->exception_number, + ARMv8Excep[elem->exception_number]); + return (resp); + case OCSD_GEN_TRC_ELEM_EXCEPTION_RET: + printf("Exception RET to %lx\n", elem->st_addr); + return (resp); + case OCSD_GEN_TRC_ELEM_TIMESTAMP: + printf("Timestamp: %lx\n", elem->timestamp); + return (resp); + case OCSD_GEN_TRC_ELEM_CYCLE_COUNT: + printf("Cycle count: %d\n", elem->cycle_count); + return (resp); + case OCSD_GEN_TRC_ELEM_EVENT: + case OCSD_GEN_TRC_ELEM_SWTRACE: + case OCSD_GEN_TRC_ELEM_SYNC_MARKER: + case OCSD_GEN_TRC_ELEM_MEMTRANS: + case OCSD_GEN_TRC_ELEM_INSTRUMENTATION: + case OCSD_GEN_TRC_ELEM_CUSTOM: + return (resp); + }; + +#if 0 + char ts[100]; + + if (elem->timestamp != 0) + sprintf(ts, "ts %ld", elem->timestamp); + else + sprintf(ts, " "); +#endif + + if (sym) { + offset = newpc - (sym->ps_start + image->pi_vaddr); + + printf("pc 0x%08lx (%lx)\t%12s\t%s+0x%lx\n", //elem->elem_type, + ip, newpc, + pmcstat_string_unintern(image->pi_name), + pmcstat_string_unintern(sym->ps_name), offset); + } else + if (image) + printf("pc 0x%08lx (%lx)\t%12s\n", //elem->elem_type, + ip, newpc, + pmcstat_string_unintern(image->pi_name)); + else { + /* image not found. */ + } + + return (resp); +} + +int +hwt_coresight_init(struct trace_context *tc) +{ + int error; + + ocsd_def_errlog_init(OCSD_ERR_SEV_INFO, 1); + + dcdtree_handle = ocsd_create_dcd_tree(OCSD_TRC_SRC_FRAME_FORMATTED, + OCSD_DFRMTR_FRAME_MEM_ALIGN); + if (dcdtree_handle == C_API_INVALID_TREE_HANDLE) { + printf("can't find dcd tree\n"); + return (-1); + } + + //cs_flags |= FLAG_FORMAT; + //cs_flags |= FLAG_FRAME_RAW_UNPACKED; + //cs_flags |= FLAG_FRAME_RAW_PACKED; + + error = create_decoder_etmv4(dcdtree_handle, tc); + if (error != OCSD_OK) { + printf("can't create decoder: tc->base %#p\n", tc->base); + return (-2); + } + +#ifdef PMCTRACE_CS_DEBUG + ocsd_tl_log_mapped_mem_ranges(dcdtree_handle); +#endif + + if (cs_flags & FLAG_FORMAT) + ocsd_dt_set_gen_elem_printer(dcdtree_handle); + else + ocsd_dt_set_gen_elem_outfn(dcdtree_handle, + gen_trace_elem_print_lookup, tc); + + attach_raw_printers(dcdtree_handle); + + return (0); +} + +int +hwt_coresight_process(struct trace_context *tc) +{ + size_t start; + size_t end; + size_t offs; + int error; + + /* Coresight data is always on CPU0 due to funnelling by HW. */ + + hwt_coresight_init(tc); + + error = hwt_get_offs(tc, &offs); +printf("OFFS %ld\n", offs); + if (error) + return (-1); + + printf("data to process %ld\n", offs); + + start = 0; + end = offs; + + cs_process_chunk(tc, start, end); + + int t; + + t = 0; + + while (1) { + hwt_sleep(); + + if (tc->terminate && t++ > 2) + break; + + error = hwt_get_offs(tc, &offs); +printf("OFFS %ld, err %d\n", offs, error); + if (error) + return (-1); + + if (offs == end) { + /* No new entries in trace. */ + hwt_sleep(); + continue; + } + + if (offs > end) { + /* New entries in the trace buffer. */ + start = end; + end = offs; + cs_process_chunk(tc, start, end); + hwt_sleep(); + continue; + } + + if (offs < end) { + /* New entries in the trace buffer. Buffer wrapped. */ + start = end; + end = tc->bufsize; + cs_process_chunk(tc, start, end); + + start = 0; + end = offs; + cs_process_chunk(tc, start, end); + + hwt_sleep(); + } + } + + return (0); +} Index: usr.sbin/hwt/hwt_elf.c =================================================================== --- /dev/null +++ usr.sbin/hwt/hwt_elf.c @@ -0,0 +1,135 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "hwtvar.h" + +int +hwt_elf_count_libs(const char *elf_path, uint32_t *nlibs0) +{ + GElf_Shdr shdr; + GElf_Phdr ph; + GElf_Ehdr eh; + Elf_Scn *scn; + Elf *elf; + size_t sh_entsize; + Elf_Data *data; + GElf_Dyn dyn; + int is_dynamic; + uint32_t nlibs; + int fd; + size_t i; + + nlibs = 0; + + assert(elf_version(EV_CURRENT) != EV_NONE); + + fd = open(elf_path, O_RDONLY, 0); + + assert(fd >= 0); + + elf = elf_begin(fd, ELF_C_READ, NULL); + + assert(elf != NULL); + assert(elf_kind(elf) == ELF_K_ELF); + + if (gelf_getehdr(elf, &eh) != &eh) { + printf("could not find elf header\n"); + return (-1); + } + + if (eh.e_type != ET_EXEC && eh.e_type != ET_DYN) { + printf("unsupported image\n"); + return (-2); + } + + if (eh.e_ident[EI_CLASS] != ELFCLASS32 && + eh.e_ident[EI_CLASS] != ELFCLASS64) + return (-3); + + is_dynamic = 0; + + for (i = 0; i < eh.e_phnum; i++) { + if (gelf_getphdr(elf, i, &ph) != &ph) { + printf("could not get program header %zu\n", i); + return (-4); + } + switch (ph.p_type) { + case PT_DYNAMIC: + is_dynamic = 1; + break; + case PT_INTERP: + nlibs++; + break; + } + } + + if (!is_dynamic) + goto done; + + scn = NULL; + data = NULL; + + while ((scn = elf_nextscn(elf, scn)) != NULL) { + assert(gelf_getshdr(scn, &shdr) == &shdr); + + if (shdr.sh_type == SHT_DYNAMIC) { + data = elf_getdata(scn, data); + assert(data != NULL); + + sh_entsize = gelf_fsize(elf, ELF_T_DYN, 1, EV_CURRENT); + + for (i = 0; i < shdr.sh_size / sh_entsize; i++) { + assert(gelf_getdyn(data, i, &dyn) == &dyn); + if (dyn.d_tag == DT_NEEDED) + nlibs++; + } + } + } + +done: + assert(elf_end(elf) == 0); + assert(close(fd) == 0); + + *nlibs0 = nlibs; + + return (0); +} Index: usr.sbin/hwt/hwt_process.c =================================================================== --- /dev/null +++ usr.sbin/hwt/hwt_process.c @@ -0,0 +1,140 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hwtvar.h" + +#include "libpmcstat_stubs.h" +#include + +struct pmcstat_image_hash_list pmcstat_image_hash[PMCSTAT_NHASH]; + +int +hwt_process_start(int *sockpair) +{ + int error; + + error = write(sockpair[PARENTSOCKET], "!", 1); + if (error != 1) + return (-1); + + return (0); +} + +static void +hwt_process_onsig(int signo) +{ + pid_t pid; + int status; + + assert(signo == SIGCHLD); + + while ((pid = wait3(&status, WNOHANG, NULL)) > 0) + if (WIFEXITED(status)) + hwt_procexit(pid, WEXITSTATUS(status)); +} + +int +hwt_process_create(int *sockpair, char **cmd, char **env __unused, int *pid0) +{ + char token; + pid_t pid; + int error; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockpair) < 0) + return (-1); + + signal(SIGCHLD, hwt_process_onsig); + + pid = fork(); + + switch (pid) { + case -1: + return (-1); + case 0: + /* Child */ + close(sockpair[PARENTSOCKET]); + + error = write(sockpair[CHILDSOCKET], "+", 1); + if (error != 1) + return (-1); + + error = read(sockpair[CHILDSOCKET], &token, 1); + if (error < 0) + return (-2); + + if (token != '!') + return (-3); + + close(sockpair[CHILDSOCKET]); + + execvp(cmd[0], cmd); + + kill(getppid(), SIGCHLD); + + exit(-3); + default: + /* Parent */ + close(sockpair[CHILDSOCKET]); + break; + } + + *pid0 = pid; + + return (0); +} + +struct pmcstat_process * +hwt_process_alloc(void) +{ + struct pmcstat_process *pp; + + pp = malloc(sizeof(struct pmcstat_process)); + if (pp) + TAILQ_INIT(&pp->pp_map); + + return (pp); +} Index: usr.sbin/hwt/hwt_record.c =================================================================== --- /dev/null +++ usr.sbin/hwt/hwt_record.c @@ -0,0 +1,117 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "hwtvar.h" + +#include "libpmcstat_stubs.h" +#include + +int +hwt_record_fetch(struct trace_context *tc, int *nrecords) +{ + struct hwt_record_user_entry *entry; + pmcstat_interned_string path; + struct pmcstat_image *image; + struct pmc_plugins plugins; + struct pmcstat_args args; + unsigned long addr; + struct hwt_record_get record_get; + int nentries; + int error; + int j; + + memset(&plugins, 0, sizeof(struct pmc_plugins)); + memset(&args, 0, sizeof(struct pmcstat_args)); + args.pa_fsroot = "/"; + nentries = 256; + + tc->records = malloc(sizeof(struct hwt_record_user_entry) * nentries); + + record_get.pid = tc->pid; + record_get.records = tc->records; + record_get.nentries = &nentries; + + error = ioctl(tc->fd, HWT_IOC_RECORD_GET, &record_get); + if (error != 0) { + printf("RECORD_GET error %d entires %d\n", + error, nentries); + return (error); + } + + for (j = 0; j < nentries; j++) { + entry = &tc->records[j]; + + if (entry->record_type > 3) { + continue; + } + + printf(" lib #%d: path %s addr %lx size %lx\n", j, + entry->fullpath, + (unsigned long)entry->addr, + entry->size); + + path = pmcstat_string_intern(entry->fullpath); + if ((image = pmcstat_image_from_path(path, 0, + &args, &plugins)) == NULL) + return (-1); + + if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) + pmcstat_image_determine_type(image, &args); + + addr = (unsigned long)entry->addr & ~1; + addr -= (image->pi_start - image->pi_vaddr); + pmcstat_image_link(tc->pp, image, addr); +#if 0 + printf("image pi_vaddr %lx pi_start %lx pi_entry %lx\n", + (unsigned long)image->pi_vaddr, + (unsigned long)image->pi_start, + (unsigned long)image->pi_entry); +#endif + } + + *nrecords = nentries; + + return (0); +} Index: usr.sbin/hwt/hwtvar.h =================================================================== --- /dev/null +++ usr.sbin/hwt/hwtvar.h @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 2023 Ruslan Bukin + * All rights reserved. + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _HWTVAR_H_ +#define _HWTVAR_H_ + +struct trace_context { + struct pmcstat_process *pp; + struct hwt_record_user_entry *records; + void *base; + int bufsize; + int pid; + int fd; + int thr_fd; + int terminate; +}; + +struct pmcstat_process *hwt_process_alloc(void); +int hwt_process_create(int *sockpair, char **cmd, char **env, int *pid0); +int hwt_process_start(int *sockpair); +int hwt_record_fetch(struct trace_context *tc, int *nrecords); +void hwt_procexit(pid_t pid, int status); +size_t hwt_get_offs(struct trace_context *tc, size_t *offs); +void hwt_sleep(void); +int hwt_elf_count_libs(const char *elf_path, uint32_t *nlibs0); + +#endif /* !_HWTVAR_H_ */ Index: usr.sbin/hwt/libpmcstat_stubs.h =================================================================== --- /dev/null +++ usr.sbin/hwt/libpmcstat_stubs.h @@ -0,0 +1,52 @@ +/*- + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include + +/* These are stubs, needed for compilation only. */ + +#ifndef _HWT_LIBPMCSTAT_STUBS_H_ +#define _HWT_LIBPMCSTAT_STUBS_H_ + +enum pmc_mode { + PMC_MODE_INVALID, +}; + +typedef int pmc_id_t; +typedef int pmc_value_t; + +struct pmclog_ev; + +int pmclog_read(void *cookie __unused, struct pmclog_ev *ev __unused); +int pmc_close_logfile(void); +int pmc_attach(pmc_id_t pmc __unused, pid_t pid __unused); + +#endif /* !_HWT_LIBPMCSTAT_STUBS_H_ */ Index: usr.sbin/hwt/libpmcstat_stubs.c =================================================================== --- /dev/null +++ usr.sbin/hwt/libpmcstat_stubs.c @@ -0,0 +1,58 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "libpmcstat_stubs.h" +#include + +/* This is a stub file, needed for compilation only. */ + +struct pmcstat_pmcs pmcstat_pmcs; +struct pmcstat_process_hash_list pmcstat_process_hash[PMCSTAT_NHASH]; + +int +pmclog_read(void *cookie __unused, struct pmclog_ev *ev __unused) +{ + + return (-1); +} + +int +pmc_close_logfile(void) +{ + + return (-1); +} + +int +pmc_attach(pmc_id_t pmc __unused, pid_t pid __unused) +{ + + return (-1); +}