Skip to content

Commit

Permalink
ubpf_inst_cnt: Add instruction counting.
Browse files Browse the repository at this point in the history
Add a new struct that can carry information about the various types of
instruction counting we wish to perform. Embed this struct into the
struct vm so it can be enabled without changing the prototypes for
_exec etc. Add an set function so library users can enable
instruction counting and get results.

In order to count the instructions executed on the underlying
bare-metal machine we leverage the perf_event framework (Linux
only). This uses ioctls to initialize and then measure the relevant
counters to ascertain how many instructions have been executing on the
CPU.

Note that access to these counters requires elevated privileges so
that may limit who can run the program in this mode.

Fixes iovisor#83.
  • Loading branch information
sbates130272 committed Sep 8, 2021
1 parent 7c8be6a commit 3b6bf79
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 2 deletions.
2 changes: 1 addition & 1 deletion vm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ all: libubpf.a test

ubpf_jit_x86_64.o: ubpf_jit_x86_64.c ubpf_jit_x86_64.h

libubpf.a: ubpf_vm.o ubpf_jit_x86_64.o ubpf_loader.o
libubpf.a: ubpf_vm.o ubpf_jit_x86_64.o ubpf_loader.o ubpf_inst_cnt.o
ar rc $@ $^

test: test.o libubpf.a
Expand Down
14 changes: 14 additions & 0 deletions vm/inc/ubpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#ifndef UBPF_H
#define UBPF_H

#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
Expand All @@ -36,6 +37,13 @@ typedef uint64_t (*ubpf_jit_fn)(void *mem, size_t mem_len);
struct ubpf_vm *ubpf_create(void);
void ubpf_destroy(struct ubpf_vm *vm);

struct ubpf_inst_cnt
{
size_t inst;
size_t inst_cmpl;
size_t inst_vm;
};

/*
* Enable / disable bounds_check
*
Expand Down Expand Up @@ -132,4 +140,10 @@ int ubpf_translate(struct ubpf_vm *vm, uint8_t *buffer, size_t *size, char **err
*/
int ubpf_set_unwind_function_index(struct ubpf_vm *vm, unsigned int idx);

/*
* Populate the struct ubpf_inst_cnt * field in struct vm so we can
* enable instruction counting.
*/
void ubpf_set_inst_cnt(struct ubpf_vm *vm, struct ubpf_inst_cnt *inst_cnt);

#endif
94 changes: 94 additions & 0 deletions vm/ubpf_inst_cnt.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright 2021 Eideticom, Inc.
*
* 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 _GNU_SOURCE
#include "ubpf_int.h"

#include <unistd.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>

#ifdef __linux__
#include <linux/perf_event.h>

void enable_instruction_count(int fd)
{
ioctl(fd, PERF_EVENT_IOC_RESET, 0);
ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
}

void disable_instruction_count(int fd)
{
ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
}

int setup_instruction_counter(void)
{
int fd;

struct perf_event_attr pe = {
.type = PERF_TYPE_HARDWARE,
.size = sizeof(struct perf_event_attr),
.config = PERF_COUNT_HW_INSTRUCTIONS,
.disabled = 1,
.exclude_kernel = 1,
.exclude_hv = 1,
};

fd = syscall(__NR_perf_event_open, &pe, 0, -1, -1, 0);

return fd;
}

long long get_instruction_count(int fd)
{
long long count;
ssize_t rd;

rd = read(fd, &count, sizeof(long long));
if (rd != sizeof(long long))
return -1;

return count;
}

#else /* __linux__ */

#include <errno.h>

void disable_instruction_count(int fd)
{
(void)fd;
}

void enable_instruction_count(int fd)
{
(void)fd;
}

int setup_instruction_counter(void)
{
errno = ENOTSUP;
return -1;
}

long long get_instruction_count(int fd)
{
(void)fd;
return -1;
}

#endif /* __linux__ */
6 changes: 6 additions & 0 deletions vm/ubpf_int.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,15 @@ struct ubpf_vm {
bool bounds_check_enabled;
int (*error_printf)(FILE* stream, const char* format, ...);
int unwind_stack_extension_index;
struct ubpf_inst_cnt *inst_cnt;
};

char *ubpf_error(const char *fmt, ...);
unsigned int ubpf_lookup_registered_function(struct ubpf_vm *vm, const char *name);

void enable_instruction_count(int fd);
void disable_instruction_count(int fd);
int setup_instruction_counter(void);
long long get_instruction_count(int fd);

#endif
47 changes: 46 additions & 1 deletion vm/ubpf_vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@ ubpf_exec(const struct ubpf_vm *vm, void *mem, size_t mem_len, uint64_t* bpf_ret
const uint16_t cur_pc = pc;
struct ebpf_inst inst = insts[pc++];

if (vm->inst_cnt) {
vm->inst_cnt->inst_vm++;
}

switch (inst.opcode) {
case EBPF_OP_ADD_IMM:
reg[inst.dst] += inst.imm;
Expand Down Expand Up @@ -595,20 +599,61 @@ int
ubpf_run(struct ubpf_vm *vm, void *mem, size_t mem_len, bool jit,
uint64_t *bpf_return_value, char **errmsg)
{
int cntrfd = 0;
ubpf_jit_fn fn;

if (vm->inst_cnt) {
cntrfd = setup_instruction_counter();
if (cntrfd == -1) {
vm->error_printf(stderr, "uBPF warning: unable to setup instruction counter\n");
}
}

if (jit) {
ubpf_jit_fn fn = ubpf_compile(vm, errmsg);
if (vm->inst_cnt) {
enable_instruction_count(cntrfd);
}
fn = ubpf_compile(vm, errmsg);
if (vm->inst_cnt) {
disable_instruction_count(cntrfd);
vm->inst_cnt->inst_cmpl = get_instruction_count(cntrfd);
if (vm->inst_cnt->inst_cmpl < 0) {
*errmsg = ubpf_error("Unable to get instruction count");
return -1;
}
}
if (fn == NULL) {
return -1;
}
}

if (vm->inst_cnt) {
enable_instruction_count(cntrfd);
}
if (jit) {
*bpf_return_value = fn(mem, mem_len);
} else {
if (ubpf_exec(vm, mem, mem_len, bpf_return_value) < 0) {
*bpf_return_value = UINT64_MAX;
}
}
if (vm->inst_cnt) {
disable_instruction_count(cntrfd);
vm->inst_cnt->inst = get_instruction_count(cntrfd);
if (vm->inst_cnt->inst < 0) {
*errmsg = ubpf_error("Unable to get instruction count");
return -1;
}
}
return 0;
}

void
ubpf_set_inst_cnt(struct ubpf_vm *vm, struct ubpf_inst_cnt *inst_cnt)
{
vm->inst_cnt = inst_cnt;
}

static bool
validate(const struct ubpf_vm *vm, const struct ebpf_inst *insts, uint32_t num_insts, char **errmsg)
{
Expand Down

0 comments on commit 3b6bf79

Please sign in to comment.