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 Aug 5, 2021
1 parent 555e750 commit 500d073
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 21 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
25 changes: 21 additions & 4 deletions vm/inc/ubpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,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 @@ -103,11 +110,14 @@ int ubpf_exec(const struct ubpf_vm *vm, void *mem, size_t mem_len, uint64_t* bpf
ubpf_jit_fn ubpf_compile(struct ubpf_vm *vm, char **errmsg);

/*
* A wrapper around some of the compile, load and run commands to
* enable instruction counting. Returns the return value of the eBPF
* program being executed.
* A wrapper around the compile, load and run commands. Returns the
* return value of the eBPF program being executed.
*
* Returns 0 on success, -1 on error. In case of error a pointer to the error
* message will be stored in 'errmsg' and should be freed by the caller.
*/
uint64_t ubpf_run(struct ubpf_vm *vm, void *mem, size_t mem_len, bool jit);
int ubpf_run(struct ubpf_vm *vm, void *mem, size_t mem_len, bool jit, uint64_t *bpf_return_value,
char **errmsg);

/*
* Translate the eBPF byte code to x64 machine code, store in buffer, and
Expand All @@ -119,4 +129,11 @@ uint64_t ubpf_run(struct ubpf_vm *vm, void *mem, size_t mem_len, bool jit);
* message will be stored in 'errmsg' and should be freed by the caller.
*/
int ubpf_translate(struct ubpf_vm *vm, uint8_t *buffer, size_t *size, char **errmsg);

/*
* 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
11 changes: 9 additions & 2 deletions vm/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,17 @@ int main(int argc, char **argv)
}

uint64_t ret;
uint64_t bpf_return_value;

ret = ubpf_run(vm, mem, mem_len, jit);
ret = ubpf_run(vm, mem, mem_len, jit, &bpf_return_value, &errmsg);
if (ret < 0) {
fprintf(stderr, "Failed to run code: %s\n", errmsg);
free(errmsg);
ubpf_destroy(vm);
return 1;
}

printf("0x%"PRIx64"\n", ret);
printf("0x%"PRIx64"\n", bpf_return_value);

ubpf_destroy(vm);

Expand Down
101 changes: 101 additions & 0 deletions vm/ubpf_inst_cnt.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* 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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdarg.h>
#include <inttypes.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include "ubpf_int.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);
if (fd == -1)
return fd;

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 @@ -32,9 +32,15 @@ struct ubpf_vm {
const char **ext_func_names;
bool bounds_check_enabled;
int (*error_printf)(FILE* stream, const char* format, ...);
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
71 changes: 57 additions & 14 deletions vm/ubpf_vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,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 @@ -573,25 +577,64 @@ ubpf_exec(const struct ubpf_vm *vm, void *mem, size_t mem_len, uint64_t* bpf_ret
}
}

uint64_t
ubpf_run(struct ubpf_vm *vm, void *mem, size_t mem_len, bool jit)
int
ubpf_run(struct ubpf_vm *vm, void *mem, size_t mem_len, bool jit,
uint64_t *bpf_return_value, char **errmsg)
{
uint64_t ret;
char *errmsg;
int cntrfd = 0;
ubpf_jit_fn fn;

if (vm->inst_cnt) {
cntrfd = setup_instruction_counter();
if (cntrfd == -1) {
*errmsg = ubpf_error("Unable to setup instruction counter");
return -1;
}
}

if (jit) {
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 setup instruction counter");
return -1;
}
}
if (fn == NULL) {
return -1;
}
}

if (vm->inst_cnt) {
enable_instruction_count(cntrfd);
}
if (jit) {
ubpf_jit_fn fn = ubpf_compile(vm, &errmsg);
if (fn == NULL) {
fprintf(stderr, "Failed to compile: %s\n", errmsg);
free(errmsg);
return 1;
}
ret = fn(mem, mem_len);
*bpf_return_value = fn(mem, mem_len);
} else {
if (ubpf_exec(vm, mem, mem_len, &ret) < 0)
ret = UINT64_MAX;
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 ret;
return 0;
}

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

static bool
Expand Down

0 comments on commit 500d073

Please sign in to comment.