Skip to content

Commit

Permalink
Prime+Scope implementation, including a "PrimeTime"
Browse files Browse the repository at this point in the history
implementation for finding prime patterns
  • Loading branch information
GalHorowitz committed Jun 4, 2022
1 parent fea5ad2 commit bec7e05
Show file tree
Hide file tree
Showing 6 changed files with 767 additions and 0 deletions.
20 changes: 20 additions & 0 deletions demo/PS-prime-time.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#include <stdio.h>
#include <stdlib.h>

#include <mastik/ps.h>
#include <mastik/pt.h>
#include <mastik/l3.h>

int main() {
pt_t patterns = pt_prepare(NULL, NULL);
pt_results_t results = generate_prime_patterns(patterns);

printf("EVCr | Cycles | Pattern\n");
for(int i = 0; i < PT_RESULTS_COUNT; i++){
printf("%7.3f%% | %6d | ", results->evcrs[i]*100, results->cycles[i]);
dump_ppattern(results->patterns[i]);
printf("\n");
}

free(results);
}
111 changes: 111 additions & 0 deletions demo/PS.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#define _GNU_SOURCE
#include <sched.h>
#undef _GNU_SOURCE

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <mastik/ps.h>
#include <mastik/low.h>

void pin_to_core(int core) {
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);
CPU_SET(core, &cpu_set);
sched_setaffinity(0, sizeof(cpu_set_t), &cpu_set);
}

enum SHARED_STATE {
STATE_INIT,
STATE_SHOULD_ACCESS,
};
volatile enum SHARED_STATE* shared_state;

pid_t setup_child(void* target) {
shared_state = mmap(NULL, sizeof(enum SHARED_STATE), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
*shared_state = STATE_INIT;

pid_t child_pid = fork();
if(child_pid == 0) {
prctl(PR_SET_PDEATHSIG, SIGKILL);
pin_to_core(3);
while(1) {
while(*shared_state == STATE_INIT) {}
mfence();
memaccesstime(target);
mfence();
*shared_state = STATE_INIT;
}
}

return child_pid;
}

void cleanup_child(pid_t pid) {
kill(pid, SIGKILL);
waitpid(pid, 0, 0);
munmap((void*)shared_state, sizeof(enum SHARED_STATE));
}

#define TARGET_LINE 31
#define TRIALS 100
int main() {
// Intel Core i5-8250U: R4_S4_P0S123S1231S23 [99.98% Success rate, 1625 cycles]
uint8_t _pat[13] = {0, PATTERN_TARGET, 1, 2, 3, PATTERN_TARGET, 1, 2, 3, 1, PATTERN_TARGET, 2, 3};
ppattern_t best_pat = construct_pattern(4, 4, _pat, sizeof(_pat));

mm_t mm = mm_prepare(NULL, NULL, NULL);
assert(mm);
ps_t ps = ps_prepare(best_pat, NULL, mm);
assert(ps);

void* target = mm_requestline(mm, L3, TARGET_LINE);
// NOTE: We are somwhat abusing this for demonstration purposes -
// we are relying on the fact that Linux uses copy-on-write for
// for MAP_PRIVATE mmap-allocations: This line comes from the
// mm->memory buffer, which is MAP_PRIVATE and so in theory it
// is not shared with the forked process, but practically it is.

ps_monitor(ps, TARGET_LINE);

pid_t child_pid = setup_child(target);
pin_to_core(2);

int success = 0;
for(int i = 0; i < TRIALS; i++) {
ps_prime(ps);

uint16_t t_res;

ps_scope(ps, &t_res);
if(t_res > L3_THRESHOLD)
continue;

ps_scope(ps, &t_res);
if(t_res > L3_THRESHOLD)
continue;

*shared_state = STATE_SHOULD_ACCESS;
mfence();
while(*shared_state == STATE_SHOULD_ACCESS) {}
mfence();

ps_scope(ps, &t_res);
if(t_res < L3_THRESHOLD)
continue;

success += 1;
}

printf("%d/%d Successful\n", success, TRIALS);

ps_release(ps);
cleanup_child(child_pid);

return 0;
}
39 changes: 39 additions & 0 deletions mastik/ps.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#ifndef __PS_H__
#define __PS_H__ 1

#include <mastik/lx.h>
#include <mastik/l3.h>

#define PATTERN_CAPACITY 20
#define PATTERN_TARGET 0xFF
struct prime_pattern {
uint8_t repeat;
uint8_t stride;
// The largest offset from i
uint8_t width;
uint8_t length;
// 0xFF denotes the target, other values denote offset from i
uint8_t pattern[PATTERN_CAPACITY];
};
typedef struct prime_pattern ppattern_t;
ppattern_t construct_pattern(uint8_t repeat, uint8_t stride, uint8_t* pattern, uint8_t length);
void dump_ppattern(ppattern_t pattern);
void access_pattern(vlist_t evset, int associativity, ppattern_t pattern);

typedef struct ps *ps_t;
ps_t ps_prepare(ppattern_t prime_pattern, l3info_t l3info, mm_t mm);

int ps_monitor(ps_t ps, int line);
int ps_unmonitor(ps_t ps, int line);
void ps_unmonitorall(ps_t ps);
int ps_getmonitoredset(ps_t ps, int *lines, int nlines);

void ps_prime(ps_t ps);
void ps_scope(ps_t ps, uint16_t* results);
// Returns the first line that is accessed, or (-1) if no monitored line
// was accessed before running through all iterations
int ps_prime_and_scope(ps_t ps, int iterations);

void ps_release(ps_t ps);

#endif // __PS_H__
22 changes: 22 additions & 0 deletions mastik/pt.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#ifndef __PRIME_TIME_H__
#define __PRIME_TIME_H__ 1

#include <mastik/ps.h>

typedef struct prime_time* pt_t;

#define PT_RESULTS_COUNT 100
struct pt_results {
ppattern_t patterns[PT_RESULTS_COUNT];
float evcrs[PT_RESULTS_COUNT];
int cycles[PT_RESULTS_COUNT];
};
typedef struct pt_results* pt_results_t;

pt_t pt_prepare(l3info_t l3info, mm_t mm);
void pt_release(pt_t pt);

// Results have to be free()-ed
pt_results_t generate_prime_patterns(pt_t patterns);

#endif // __PRIME_TIME_H__
Loading

0 comments on commit bec7e05

Please sign in to comment.