├── .gitignore ├── experiments ├── results │ ├── plot_scripts │ │ ├── test │ │ │ └── pids.txt │ │ ├── plot_victim.py │ │ ├── plot_repl.py │ │ ├── compatibility.py │ │ ├── proc_maps_parse.py │ │ └── plot.py │ └── Time_matrix.xlsx ├── .DS_Store ├── run_cmd.txt ├── Makefile ├── e1_benchmark.c ├── e1_benchmark1.c ├── e2_benchmark.c ├── e1_benchmark2.c ├── space_overhead.sh ├── params.h ├── legacy │ ├── e1_run_experiment.c │ ├── syn_multiple.c │ └── run_sd_vbs.c └── snapshot.c ├── Makefile ├── dumpcache.mod.c ├── README.md └── dumpcache.c /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /experiments/results/plot_scripts/test/pids.txt: -------------------------------------------------------------------------------- 1 | 13 2 | 9049 3 | 9050 4 | -------------------------------------------------------------------------------- /experiments/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weirdindiankid/cacheflow/HEAD/experiments/.DS_Store -------------------------------------------------------------------------------- /experiments/results/Time_matrix.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weirdindiankid/cacheflow/HEAD/experiments/results/Time_matrix.xlsx -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | obj-m += dumpcache.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | -------------------------------------------------------------------------------- /experiments/run_cmd.txt: -------------------------------------------------------------------------------- 1 | sudo ./snapshot "./sd-vbs-bin/tracking/data/vga/tracking ./sd-vbs-bin/tracking/data/vga/" "./sd-vbs-bin/mser/data/vga/mser ./sd-vbs-bin/mser/data/vga/" "./sd-vbs-bin/sift/data/vga/sift ./sd-vbs-bin/sift/data/vga/" "./sd-vbs-bin/disparity/data/vga/disparity ./sd-vbs-bin/disparity/data/vga/" -o /tmp/mc_4sdvbs_vga -fr -p 10 2 | -------------------------------------------------------------------------------- /dumpcache.mod.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | MODULE_INFO(vermagic, VERMAGIC_STRING); 6 | MODULE_INFO(name, KBUILD_MODNAME); 7 | 8 | __visible struct module __this_module 9 | __attribute__((section(".gnu.linkonce.this_module"))) = { 10 | .name = KBUILD_MODNAME, 11 | .init = init_module, 12 | #ifdef CONFIG_MODULE_UNLOAD 13 | .exit = cleanup_module, 14 | #endif 15 | .arch = MODULE_ARCH_INIT, 16 | }; 17 | 18 | static const char __module_depends[] 19 | __used 20 | __attribute__((section(".modinfo"))) = 21 | "depends="; 22 | 23 | -------------------------------------------------------------------------------- /experiments/Makefile: -------------------------------------------------------------------------------- 1 | all: clean e1_benchmark e1_benchmark1 e1_benchmark2 e2_benchmark snapshot 2 | 3 | snapshot: snapshot.c 4 | gcc -Wall -o e1_run_experiment e1_run_experiment.c 5 | 6 | e1_benchmark: e1_benchmark.c 7 | gcc -o e1_benchmark e1_benchmark.c 8 | 9 | e1_benchmark1: e1_benchmark1.c 10 | gcc -o e1_benchmark1 e1_benchmark1.c 11 | 12 | e1_benchmark2: e1_benchmark2.c 13 | gcc -o e1_benchmark2 e1_benchmark2.c 14 | 15 | e2_benchmark: e2_benchmark.c 16 | gcc -o e2_benchmark e2_benchmark.c 17 | 18 | clean: 19 | rm -f e1_benchmark e1_benchmark1 e1_benchmark2 e2_benchmark snapshot 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## CacheFlow 2 | ### Authors: Dharmesh Tarapore, Renato Mancuso, Shahin Roozkhosh, Steven Brzozowski 3 |
4 |

5 | CacheFlow is a Linux kernel module meant to run on ARM CPUs. 6 |
It snapshots the contents of the L2 cache, which can then be used to better understand the underlying behavior of the cache. 7 |
Additional details forthcoming. 8 |

9 |
10 |

11 | D. Tarapore, S. Roozhkhosh, S. Brzozowski and R. Mancuso, "Observing the Invisible: Live Cache Inspection for High-Performance Embedded Systems," in IEEE Transactions on Computers, doi: 10.1109/TC.2021.3060650 12 |

13 | -------------------------------------------------------------------------------- /experiments/e1_benchmark.c: -------------------------------------------------------------------------------- 1 | #include "params.h" 2 | 3 | int main() { 4 | char *buf; 5 | int i, a; 6 | int iterations = 0; 7 | buf = (char*) malloc(BASE_BUFFSIZE_MB * 1024 * 1024); 8 | while (iterations < NUM_ITERATIONS) { 9 | // Write to 1 mb buffer 10 | for (i = 0; i < BASE_BUFFSIZE_MB * 1024 * 1024; i = i + 4) { 11 | buf[i] = '\xFE'; 12 | buf[i + 1] = '\xCA'; 13 | buf[i + 2] = '\xEF'; 14 | buf[i + 3] = '\xBE'; 15 | } 16 | // Read from buffer 17 | for (i = 0; i < BASE_BUFFSIZE_MB * 1024 * 1024; i = i + 4) { 18 | a = buf[i] + buf[i + 1] + buf[i + 2] + buf[i + 3]; 19 | } 20 | // usleep(1000); 21 | iterations++; 22 | } 23 | free(buf); 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /experiments/e1_benchmark1.c: -------------------------------------------------------------------------------- 1 | #include "params.h" 2 | 3 | int main() { 4 | char *buf; 5 | int i, a; 6 | int iterations = 0; 7 | buf = (char*) malloc((BASE_BUFFSIZE_MB / 2) * 1024 * 1024); 8 | while (iterations < (NUM_ITERATIONS * 2)) { 9 | // Write to 1 mb buffer 10 | for (i = 0; i < (BASE_BUFFSIZE_MB / 2) * 1024 * 1024; i = i + 4) { 11 | buf[i] = '\xFE'; 12 | buf[i + 1] = '\xCA'; 13 | buf[i + 2] = '\xEF'; 14 | buf[i + 3] = '\xBE'; 15 | } 16 | // Read from buffer 17 | for (i = 0; i < (BASE_BUFFSIZE_MB / 2) * 1024 * 1024; i = i + 4) { 18 | a = buf[i] + buf[i + 1] + buf[i + 2] + buf[i + 3]; 19 | } 20 | // usleep(1000); 21 | iterations++; 22 | } 23 | free(buf); 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /experiments/e2_benchmark.c: -------------------------------------------------------------------------------- 1 | #include "params.h" 2 | 3 | void read_memory_map(void) { 4 | char input_filename[20]; 5 | FILE* mmap_fp; 6 | FILE* output_fp; 7 | char s[100]; 8 | 9 | mmap_fp = fopen("/proc/self/maps", "r"); 10 | if (mmap_fp == NULL) { 11 | perror("Can't open mmap_fp"); 12 | } 13 | output_fp = fopen("/tmp/dumpcache/mmap.txt", "w+"); 14 | if (output_fp == NULL) { 15 | perror("Can't open output_fp"); 16 | } 17 | 18 | while (fgets(s, 100, mmap_fp) != NULL) { 19 | fputs(s, output_fp); 20 | } 21 | 22 | fclose(mmap_fp); 23 | fclose(output_fp); 24 | return; 25 | } 26 | 27 | int main() { 28 | char *buf; 29 | int i, a, j; 30 | int iterations = 0; 31 | buf = (char*) malloc(BASE_BUFFSIZE_MB * 1024 * 1024); 32 | while (iterations < NUM_ITERATIONS) { 33 | j = 0; 34 | if (iterations > 5){ 35 | j = (BASE_BUFFSIZE_MB * 1024 * 1024)/2; 36 | } 37 | 38 | for (i=j; i < BASE_BUFFSIZE_MB * 1024 * 1024; i = i + 4) { 39 | buf[i] = '\xFE'; 40 | buf[i + 1] = '\xCA'; 41 | buf[i + 2] = '\xEF'; 42 | buf[i + 3] = '\xBE'; 43 | } 44 | // Read from buffer 45 | for (i=j; i < BASE_BUFFSIZE_MB * 1024 * 1024; i = i + 4) { 46 | a = buf[i] + buf[i + 1] + buf[i + 2] + buf[i + 3]; 47 | } 48 | // usleep(1000); 49 | iterations++; 50 | } 51 | 52 | free(buf); 53 | read_memory_map(); 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /experiments/e1_benchmark2.c: -------------------------------------------------------------------------------- 1 | #include "params.h" 2 | 3 | int main() { 4 | char *buf; 5 | int i, a; 6 | int iterations = 0; 7 | buf = (char*) malloc(BASE_BUFFSIZE_MB * 1024 * 1024); 8 | while (iterations < NUM_ITERATIONS) { 9 | // Write to 1 mb buffer 10 | //printf("I want to write %.2f of the buffer\r\n", ((float)iterations+1)/NUM_ITERATIONS); 11 | //BASE_BUFFSIZE_MB * 1024 * 1024 12 | for (i = 0; i < (((float)iterations+1)/NUM_ITERATIONS) * (BASE_BUFFSIZE_MB * 1024 * 1024); i = i + 4) { 13 | buf[i] = '\xFE'; 14 | buf[i + 1] = '\xCA'; 15 | buf[i + 2] = '\xEF'; 16 | buf[i + 3] = '\xBE'; 17 | } 18 | // Read from buffer 19 | for (i = 0; i < (((float)iterations+1)/NUM_ITERATIONS) * (BASE_BUFFSIZE_MB * 1024 * 1024); i = i + 4) { 20 | a = buf[i] + buf[i + 1] + buf[i + 2] + buf[i + 3]; 21 | } 22 | // usleep(1000); 23 | iterations++; 24 | } 25 | free(buf); 26 | return 0; 27 | } 28 | 29 | 30 | 31 | // #include "params.h" 32 | 33 | // /* 34 | // Read 1/3 buf first 35 | // Then 2/3 buf 36 | // Then all of the buffer 37 | // */ 38 | 39 | // int main() { 40 | // char *buf; 41 | // int i, a; 42 | // int iterations = 0; 43 | // buf = (char*) malloc((BASE_BUFFSIZE_MB / 4) * 1024 * 1024); 44 | // while (iterations < (NUM_ITERATIONS * 4)) { 45 | // // Write to 1 mb buffer 46 | // for (i = 0; i < ((iterations + 1)/(BASE_BUFFSIZE_MB)) * (BASE_BUFFSIZE_MB / 4) * 1024 * 1024; i = i + 4) { 47 | // buf[i] = '\xFE'; 48 | // buf[i + 1] = '\xCA'; 49 | // buf[i + 2] = '\xEF'; 50 | // buf[i + 3] = '\xBE'; 51 | // } 52 | // // Read from buffer 53 | // for (i = 0; i < ((iterations + 1)/(BASE_BUFFSIZE_MB)) * (BASE_BUFFSIZE_MB / 4) * 1024 * 1024; i = i + 4) { 54 | // a = buf[i] + buf[i + 1] + buf[i + 2] + buf[i + 3]; 55 | // } 56 | // // usleep(1000); 57 | // iterations++; 58 | // } 59 | // free(buf); 60 | // return 0; 61 | // } 62 | -------------------------------------------------------------------------------- /experiments/space_overhead.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Script to automate acquisition of space overhead data 4 | 5 | bm="./sd-vbs-bin/disparity/data/vga/disparity ./sd-vbs-bin/disparity/data/vga/" 6 | period=100 7 | 8 | ## Test with in-line snapshot storage, address resoltuion, layout acquisition 9 | echo "Flush Mode" 10 | for i in $(seq 30) 11 | do 12 | 13 | sudo ./snapshot "$bm" -o /tmp/overhead -fr -p $period -h 1>/dev/null 2>/dev/null 14 | difflines=$(sudo diff /tmp/overhead/cachedump1.csv /tmp/overhead/cachedump2.csv | grep "^>" | wc -l) 15 | totlines=$(sudo cat /tmp/overhead/cachedump1.csv | wc -l) 16 | python -c "print(100 * float($difflines)/$totlines)" 17 | done 18 | 19 | ## Test with address resoltuion, layout acquisition (transparent mode) 20 | echo "Resolve+Layout Mode" 21 | for i in $(seq 30) 22 | do 23 | sudo ./snapshot "$bm" -o /tmp/overhead -fr -p $period -t -h 1>/dev/null 2>/dev/null 24 | difflines=$(sudo diff /tmp/overhead/cachedump1.csv /tmp/overhead/cachedump2.csv | grep "^>" | wc -l) 25 | python -c "print(100 * float($difflines)/$totlines)" 26 | done 27 | 28 | ## Test with address resoltuion, (transparent mode) 29 | echo "Resolve Mode" 30 | for i in $(seq 30) 31 | do 32 | sudo ./snapshot "$bm" -o /tmp/overhead -fr -p $period -t -l -h 1>/dev/null 2>/dev/null 33 | difflines=$(sudo diff /tmp/overhead/cachedump1.csv /tmp/overhead/cachedump2.csv | grep "^>" | wc -l) 34 | python -c "print(100 * float($difflines)/$totlines)" 35 | done 36 | 37 | echo "Layout Mode" 38 | ## Test with layout acquisition, (transparent mode) 39 | for i in $(seq 30) 40 | do 41 | sudo ./snapshot "$bm" -o /tmp/overhead -fr -p $period -t -n -h 1>/dev/null 2>/dev/null 42 | difflines=$(sudo diff /tmp/overhead/cachedump1.csv /tmp/overhead/cachedump2.csv | grep "^>" | wc -l) 43 | python -c "print(100 * float($difflines)/$totlines)" 44 | done 45 | 46 | echo "Full Transparent Mode" 47 | ## Test in full transparent mode 48 | for i in $(seq 30) 49 | do 50 | sudo ./snapshot "$bm" -o /tmp/overhead -fr -p $period -t -n -l -h 1>/dev/null 2>/dev/null 51 | difflines=$(sudo diff /tmp/overhead/cachedump1.csv /tmp/overhead/cachedump2.csv | grep "^>" | wc -l) 52 | python -c "print(100 * float($difflines)/$totlines)" 53 | done 54 | 55 | -------------------------------------------------------------------------------- /experiments/results/plot_scripts/plot_victim.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | from scipy.stats import norm 4 | import matplotlib.gridspec as gridspec 5 | import scipy.stats 6 | 7 | sequences=[] 8 | 9 | def mean_confidence_interval(data, confidence=0.95): 10 | a = 1.0 * np.array(data) 11 | n = len(a) 12 | m, se = np.mean(a), scipy.stats.sem(a) 13 | h = se * scipy.stats.t.ppf((1 + confidence) / 2., n-1) 14 | return m, m-h, m+h 15 | 16 | cached = [] 17 | fig = plt.figure(figsize=(6, 3)) 18 | 19 | plt.xticks(range(0,17)) 20 | #plt.title('Probability of X Lines Cached after Y Iterations') 21 | 22 | plots = 8 23 | 24 | cmap=plt.get_cmap("viridis", plots) 25 | colors=cmap.colors 26 | 27 | ways=[0] * 16 28 | total=0 29 | 30 | idx = 0 31 | rnd = [] 32 | rounds = [] 33 | 34 | f = open("period.txt", "r") 35 | for l in f.readlines(): 36 | if l == "\n": 37 | if (idx == 16): 38 | if len(rnd) == 16: 39 | rounds.append(list(rnd)) 40 | print rnd 41 | rnd = [] 42 | idx = 0 43 | else: 44 | idx += 1 45 | continue 46 | 47 | total += 1 48 | cur = int(l.split(":")[0]) 49 | ways[cur-1] += 1 50 | 51 | rnd.append(cur) 52 | idx += 1 53 | 54 | for w in range(len(ways)): 55 | ways[w] = ways[w]/float(total) 56 | 57 | 58 | plt.bar(range(1,17), ways, label="Observed Replacement") 59 | plt.plot(range(0,18), [float(1.0/16)]*18, ls=":", color="red", label="Ideal Replacement") 60 | plt.legend(ncol=2, loc="upper center", borderaxespad=0., 61 | bbox_to_anchor=(0., 1.02, 1., .102),) 62 | plt.xlim(0.3,16.7) 63 | plt.xlabel("Way Selected for Replacement") 64 | plt.ylabel("Probability") 65 | plt.show() 66 | 67 | fig.savefig("victim_prob.png", dpi=fig.dpi, bbox_inches='tight'); 68 | fig.savefig("victim_prob.pdf", dpi=fig.dpi, bbox_inches='tight'); 69 | 70 | # cached.append([]) 71 | 72 | # leg.append(ax.bar(x, cached[r-1], color=colors[r-1])) 73 | # ax.set_yticks([0.1, 0.3, 0.5]) 74 | # ax.set_ylim(0, 0.5) 75 | 76 | # labels.append(str(r) + " iter.") 77 | 78 | # if r == plots/2: 79 | # ax.set_ylabel('Probablity') 80 | 81 | # ax.set_xticks(range(1,17)) 82 | # #ax.set_xlim(1,16) 83 | # ax.set_xlabel('Number of Cached Lines') 84 | 85 | # fig.legend(leg, # The line objects 86 | # ncol=4, 87 | # labels=labels, # The labels for each line 88 | # loc="upper center", # Position of legend 89 | # borderaxespad=0.1, # Small spacing around legend box 90 | # ) 91 | 92 | # plt.tight_layout() 93 | # plt.show() 94 | 95 | # fig.savefig("repl_prob.png", dpi=fig.dpi, bbox_inches='tight'); 96 | # fig.savefig("repl_prob.pdf", dpi=fig.dpi, bbox_inches='tight'); 97 | -------------------------------------------------------------------------------- /experiments/params.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define __STR(x) #x 14 | #define STR(x) __STR(x) 15 | 16 | // Each cache set/way coordinate has a 1-5 digit pid followed by a 17 | // comma then 12 digit tag prepended with '0x' and ended with a newline 18 | #define CSV_LINE_SIZE 5 + 1 + (2 + 12) + 1 19 | #define WRITE_SIZE 35 * 1024 // 32 kb (8 4kb pages) 20 | #define NUM_CACHESETS 2048 21 | #define CACHESIZE 1024*1024*2 22 | #define NUM_CACHELINES 16 23 | #define PROC_FILENAME "/proc/dumpcache" 24 | 25 | #define SCRATCHSPACE_DIR "/tmp/dumpcache" 26 | #define PIPENV_DIR "/home/nvidia/.local/bin/pipenv" 27 | #define MICROSECONDS_IN_MILLISECONDS 1000 28 | #define MILLISECONDS_BETWEEN_SAMPLES 10 * MICROSECONDS_IN_MILLISECONDS 29 | 30 | /* SD-VBS Params */ 31 | //#define NUM_SD_VBS_BENCHMARKS 1 32 | /* Set to 7 to run all */ 33 | //#define NUM_SD_VBS_BENCHMARKS_DATASETS 2 34 | 35 | 36 | // Struct representing a single cache line - each cacheline struct is 68 bytes 37 | struct cache_line 38 | { 39 | pid_t pid; 40 | uint64_t addr; 41 | }; 42 | 43 | struct cache_set { 44 | struct cache_line cachelines[NUM_CACHELINES]; 45 | }; 46 | 47 | struct cache_sample { 48 | struct cache_set sets[NUM_CACHESETS]; 49 | }; 50 | 51 | #define NUM_ITERATIONS 3 52 | #define BASE_BUFFSIZE_MB 2.0 53 | 54 | 55 | /* Defines for commands to the kernel module */ 56 | /* Command to access the configuration interface */ 57 | #define DUMPCACHE_CMD_CONFIG _IOW(0, 0, unsigned long) 58 | /* Command to initiate a cache dump */ 59 | #define DUMPCACHE_CMD_SNAPSHOT _IOW(0, 1, unsigned long) 60 | 61 | #define DUMPCACHE_CMD_VALUE_WIDTH 16 62 | #define DUMPCACHE_CMD_VALUE_MASK ((1 << DUMPCACHE_CMD_VALUE_WIDTH) - 1) 63 | #define DUMPCACHE_CMD_VALUE(cmd) \ 64 | (cmd & DUMPCACHE_CMD_VALUE_MASK) 65 | 66 | /* Command to set the current buffer number */ 67 | #define DUMPCACHE_CMD_SETBUF_SHIFT (1 << (DUMPCACHE_CMD_VALUE_WIDTH + 1)) 68 | 69 | /* Command to retrievet the current buffer number */ 70 | #define DUMPCACHE_CMD_GETBUF_SHIFT (1 << (DUMPCACHE_CMD_VALUE_WIDTH + 2)) 71 | 72 | /* Command to enable/disable buffer autoincrement */ 73 | #define DUMPCACHE_CMD_AUTOINC_EN_SHIFT (1 << (DUMPCACHE_CMD_VALUE_WIDTH + 3)) 74 | #define DUMPCACHE_CMD_AUTOINC_DIS_SHIFT (1 << (DUMPCACHE_CMD_VALUE_WIDTH + 4)) 75 | 76 | /* Command to enable/disable address resolution */ 77 | #define DUMPCACHE_CMD_RESOLVE_EN_SHIFT (1 << (DUMPCACHE_CMD_VALUE_WIDTH + 5)) 78 | #define DUMPCACHE_CMD_RESOLVE_DIS_SHIFT (1 << (DUMPCACHE_CMD_VALUE_WIDTH + 6)) 79 | 80 | /* Command to enable/disable snapshot timestamping */ 81 | #define DUMPCACHE_CMD_TIMESTAMP_EN_SHIFT (1 << (DUMPCACHE_CMD_VALUE_WIDTH + 7)) 82 | #define DUMPCACHE_CMD_TIMESTAMP_DIS_SHIFT (1 << (DUMPCACHE_CMD_VALUE_WIDTH + 8)) 83 | -------------------------------------------------------------------------------- /experiments/results/plot_scripts/plot_repl.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | from matplotlib.lines import Line2D 3 | import numpy as np 4 | from scipy.stats import norm 5 | import matplotlib.gridspec as gridspec 6 | import statistics 7 | 8 | 9 | cached = [] 10 | fig = plt.figure(figsize=(6, 5)) 11 | 12 | plt.xticks(range(0,17)) 13 | plt.title('Probability of X Lines Cached after Y Iterations') 14 | 15 | plots = 8 16 | 17 | cmap=plt.get_cmap("viridis", plots) 18 | colors=cmap.colors 19 | 20 | gs1 = gridspec.GridSpec(plots, 1) 21 | gs1.update(wspace=0.025, hspace=0.2) # set the spacing between axes. 22 | 23 | ax = plt.subplot(gs1[0]) 24 | 25 | leg = [] 26 | labels=[] 27 | 28 | fitlab = "Fit" 29 | handles = [] 30 | labels = [] 31 | 32 | fithandle = None 33 | 34 | for r in range(1, plots+1): 35 | 36 | ax = plt.subplot(gs1[r-1], sharey=ax) 37 | 38 | f = open("pol"+str(r)+".txt", "r") 39 | cached.append([]) 40 | raw = [] 41 | 42 | for i in range(1, 17): 43 | cached[r-1].append(0) 44 | 45 | for l in f.readlines(): 46 | cur = int(l) 47 | if cur > 15: 48 | continue 49 | cached[r-1][cur] += 1 50 | raw.append(cur+1) 51 | 52 | print cached[r-1] 53 | 54 | #ax.axvline(statistics.median(raw)) 55 | norm_cached = [0] * len(cached[r-1]) 56 | 57 | for l in range(len(cached[r-1])): 58 | norm_cached[l] = float(cached[r-1][l]) / (sum(cached[r-1])) 59 | 60 | x = range(1,17) 61 | ax.bar(x, norm_cached, color=colors[r-1], label=(str(r) + " iter.")) 62 | ax.set_yticks([0.1, 0.3, 0.5]) 63 | ax.set_ylim(0, 0.5) 64 | 65 | if r == plots/2: 66 | ax.set_ylabel('Probability') 67 | 68 | ax.set_xticks(range(1,17)) 69 | 70 | mean,std=norm.fit(raw) 71 | 72 | x = np.linspace(1, 16, 1000) 73 | p = norm.pdf(x, mean, std) 74 | 75 | # if r == 16: 76 | # leg.append(ax.plot(x, p, linewidth=1, color='r', ls="--", label="Test")) 77 | # else: 78 | 79 | handles.append(ax.get_legend_handles_labels()[0][0]) 80 | labels.append(ax.get_legend_handles_labels()[1][0]) 81 | 82 | print len(ax.get_legend_handles_labels()[0]) 83 | 84 | if r == 8: 85 | ax.plot(x, p, linewidth=1, color='r', ls="--", label="Fit") 86 | 87 | handles.append(ax.get_legend_handles_labels()[0][0]) 88 | labels.append(ax.get_legend_handles_labels()[1][0]) 89 | 90 | else: 91 | ax.plot(x, p, linewidth=1, color='r', ls="--", label="_nolegend_") 92 | 93 | 94 | ax.set_xticks(range(1,17)) 95 | #ax.set_xlim(1,16) 96 | ax.set_xlabel('Number of Cached Lines ($k$)') 97 | 98 | 99 | # # manually define a new patch 100 | # fitline = Line2D([0], [0], color="r", linewidth=1, linestyle='--') 101 | 102 | # # handles is a list, so append manual patch 103 | # handles.append(fitline) 104 | # labels.append("Fit") 105 | 106 | # print labels 107 | 108 | 109 | fig.legend(handles=handles,ncol=5,labels=labels, 110 | loc="upper center", # Position of legend 111 | borderaxespad=0.1, # Small spacing around legend box 112 | ) 113 | 114 | 115 | plt.tight_layout() 116 | plt.show() 117 | 118 | fig.savefig("repl_prob.png", dpi=fig.dpi, bbox_inches='tight'); 119 | fig.savefig("repl_prob.pdf", dpi=fig.dpi, bbox_inches='tight'); 120 | -------------------------------------------------------------------------------- /experiments/results/plot_scripts/compatibility.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | ####################################################### 4 | # # 5 | # Cache snapshot plotting functions # 6 | # # 7 | # Author: Renato Mancuso (BU) # 8 | # Date: April 2020 # 9 | # # 10 | ####################################################### 11 | 12 | import math 13 | import os 14 | import os.path 15 | import sys 16 | import numpy as np 17 | import commands 18 | import operator 19 | import shutil 20 | import matplotlib.pyplot as plt 21 | 22 | from proc_maps_parse import * 23 | 24 | path="../sdvbs_solo/" 25 | bms=["disparity", "mser", "sift", "tracking", "bomb"] 26 | spects={} 27 | pids={} 28 | quotas={} 29 | reuse={} 30 | 31 | tot_lines = float(2 * 1024 * 1024 / 64) 32 | 33 | def plot_quota(b, ax): 34 | x = range(1, len(quotas[b])+1) 35 | y = quotas[b] 36 | 37 | ax.set(title=(b + " - Active"), ylabel='L2 lines (%)') 38 | ax.set_ylim(0, 1) 39 | ax.plot(x, y) 40 | ax.fill_between(x, y, 0, alpha=0.5) 41 | plt.setp(ax.get_xticklabels(), visible=False) 42 | 43 | def plot_reuse(b, ax): 44 | x = range(1, len(reuse[b])+1) 45 | y = reuse[b] 46 | 47 | ax.set(title=(b + " - Reused")) 48 | ax.set_ylim(0, 1) 49 | ax.plot(0,0) 50 | ax.plot(x, y) 51 | ax.fill_between(x, y, 0, alpha=0) 52 | ax.fill_between(x, y, 0, alpha=0.5) 53 | plt.setp(ax.get_xticklabels(), visible=False) 54 | 55 | for b in bms: 56 | spects[b] = MemSpectrum(0, path + b + "/pids.txt", 1, 500) 57 | pids[b] = spects[b].other_pids[-1] 58 | quotas[b] = [] 59 | reuse[b] = [] 60 | 61 | for b in bms: 62 | for a in bms: 63 | b_spect = spects[b] 64 | a_spect = spects[a] 65 | 66 | b_pid = pids[b] 67 | a_pid = pids[a] 68 | 69 | # Get the number of snapshots for this bm 70 | count = len(b_spect.dumps) 71 | 72 | tot_quota = 0.0 73 | tot_reused = 0.0 74 | ua_prev_dump = None 75 | in_prev_dump = None 76 | 77 | for i, d in enumerate(b_spect.dumps): 78 | ua_quota = d.pid_accesses[b_pid].get_count() / tot_lines 79 | 80 | if(len(quotas[b]) < len(b_spect.dumps)): 81 | quotas[b].append(ua_quota) 82 | 83 | ua_reused = 0 84 | in_quota = 0 85 | in_reused = 0 86 | 87 | if ua_prev_dump != None: 88 | ua_reused = d.pid_accesses[b_pid].get_reused_blocks(ua_prev_dump.pid_accesses[b_pid]) 89 | 90 | if(len(reuse[b]) < len(b_spect.dumps)): 91 | reuse[b].append(ua_reused / tot_lines) 92 | 93 | if i < len(a_spect.dumps): 94 | in_d = a_spect.dumps[i] 95 | in_quota = in_d.pid_accesses[a_pid].get_count() / tot_lines 96 | in_reused = 0 97 | 98 | if in_prev_dump != None: 99 | in_reused = in_d.pid_accesses[a_pid].get_reused_blocks(in_prev_dump.pid_accesses[a_pid]) 100 | 101 | in_prev_dump = in_d 102 | ua_prev_dump = d 103 | 104 | # Compute quota exceeding cache size 105 | tot_quota += max(0, (ua_quota + in_quota) - 1.0) 106 | tot_reused += (ua_reused / tot_lines) * in_quota 107 | 108 | max_quota = 1.0 * len(b_spect.dumps) 109 | 110 | print "BM %s intefered by %s: %f (reused = %f)" % (b, a, float(tot_quota) / max_quota, float(tot_reused) / max_quota) 111 | 112 | i = 1 113 | for b in bms: 114 | 115 | ax = plt.subplot(len(bms), 2, i) 116 | plot_quota(b, ax) 117 | i+=1 118 | 119 | ax = plt.subplot(len(bms), 2, i) 120 | plot_reuse(b, ax) 121 | i+=1 122 | 123 | fig = plt.gcf() 124 | fig.set_size_inches(5.5, 6) 125 | plt.tight_layout() 126 | plt.show() 127 | 128 | fig.savefig("characteristics.png", dpi=fig.dpi, bbox_inches='tight'); 129 | fig.savefig("characteristics.pdf", dpi=fig.dpi, bbox_inches='tight'); 130 | -------------------------------------------------------------------------------- /experiments/legacy/e1_run_experiment.c: -------------------------------------------------------------------------------- 1 | #include "params.h" 2 | 3 | int round_index; 4 | 5 | int read_cache_to_file(char* filename) { 6 | 7 | int outfile; 8 | int dumpcache_fd; 9 | int bytes_to_write = 0; 10 | struct cache_set* cache_contents; 11 | 12 | char csv_file_buf[WRITE_SIZE + 2*CSV_LINE_SIZE]; 13 | int cache_set_idx, cache_line_idx; 14 | 15 | cache_contents = NULL; 16 | 17 | if (((outfile = open(filename, O_CREAT | O_WRONLY | O_SYNC | O_TRUNC, 0666)) < 0)) { 18 | perror("Failed to open outfile"); 19 | exit(1); 20 | } 21 | 22 | if (!cache_contents) { 23 | cache_contents = (struct cache_set*) malloc(sizeof(struct cache_set) * NUM_CACHESETS); 24 | } 25 | 26 | if (((dumpcache_fd = open(PROC_FILENAME, O_RDONLY)) < 0)) { 27 | perror("Failed to open proc file"); 28 | exit(1); 29 | } 30 | 31 | if (read(dumpcache_fd, cache_contents, sizeof(struct cache_set) * NUM_CACHESETS) < 0) { 32 | perror("Failed to read from proc file"); 33 | exit(1); 34 | } 35 | 36 | for (cache_set_idx = 0; cache_set_idx < NUM_CACHESETS; cache_set_idx++) { 37 | for (cache_line_idx = 0; cache_line_idx < NUM_CACHELINES; cache_line_idx++) { 38 | 39 | int d = sprintf(csv_file_buf + bytes_to_write, "%05d,0x%012lx\n", 40 | cache_contents[cache_set_idx].cachelines[cache_line_idx].pid, 41 | cache_contents[cache_set_idx].cachelines[cache_line_idx].addr); 42 | //printf("d is: %d\r\n", d); 43 | bytes_to_write += d; 44 | //bytes_to_write += CSV_LINE_SIZE; 45 | if (bytes_to_write >= WRITE_SIZE){ 46 | //printf("On line 84, bytes_to_write is %d\r\n", bytes_to_write); 47 | if (write(outfile, csv_file_buf, bytes_to_write) == -1) { 48 | perror("Failed to write to outfile"); 49 | } 50 | bytes_to_write = 0; 51 | } 52 | } 53 | } 54 | 55 | if (bytes_to_write){ 56 | // Write remaining amount 57 | //printf("%s%d\n", "Bytes to write:", bytes_to_write); 58 | if (write(outfile, csv_file_buf, bytes_to_write) == -1) { 59 | perror("Failed to write to outfile"); 60 | } 61 | } 62 | 63 | close(outfile); 64 | close(dumpcache_fd); 65 | return 0; 66 | } 67 | 68 | void dump_proc_map(pid_t* proc_ids, int argc) { 69 | int i = 0; 70 | char cbuf[100]; 71 | for(i = 0; i < argc -1; i++) { 72 | sprintf(cbuf, "cat /proc/%d/maps > /tmp/dumpcache/%d-%d.txt", proc_ids[i], proc_ids[i], round_index); 73 | system(cbuf); 74 | } 75 | } 76 | 77 | void dumpcache(void) { 78 | char filename[100]; 79 | 80 | if (round_index == 0) { 81 | // Make folder to be used as scratch space on first iteration 82 | struct stat st = {0}; 83 | if (stat(SCRATCHSPACE_DIR, &st) == -1) { 84 | mkdir(SCRATCHSPACE_DIR, 0700); 85 | } 86 | } 87 | printf("Dumping cache to CSV files (round: %d)\n", round_index); 88 | fflush(stdout); 89 | 90 | sprintf(filename, "%s/cachedump%d.csv", SCRATCHSPACE_DIR, round_index++); 91 | read_cache_to_file(filename); 92 | } 93 | 94 | int main(int argc, char* argv[]) { 95 | 96 | char command[100]; 97 | char* executable; 98 | int finished; 99 | int i; 100 | int wstatus; 101 | pid_t cpid; 102 | pid_t* cpid_arr; 103 | 104 | if (argc < 2) { 105 | printf("Please enter a list of executables to run while sampling the cache.\n"); 106 | exit(1); 107 | } 108 | 109 | cpid_arr = malloc(sizeof(pid_t) * (argc - 1)); 110 | 111 | for (i = 0; i < (argc - 1); i++) { /* Start by execing all experiments */ 112 | 113 | executable = argv[i + 1]; /* Executables start at argc[1] */ 114 | cpid = fork(); 115 | if (cpid == -1) { 116 | perror("fork"); 117 | exit(EXIT_FAILURE); 118 | } 119 | if (cpid == 0) { /* Code executed by child */ 120 | sprintf(command, "./%s", executable); 121 | printf("command: %s\r\n", command); 122 | //system(command); 123 | execl(command, command, NULL); 124 | exit(EXIT_FAILURE); 125 | } 126 | else { /* Code executed by parent */ 127 | // If we have 2 executables, one of these will run on CPU core 0 and the other one will 128 | // run on CPU core 1 129 | /*if(argc >= 3) { 130 | if(i == 1) { sprintf(command,"sudo taskset -p 0 "); } 131 | }*/ 132 | cpid_arr[i] = cpid; 133 | } 134 | } 135 | 136 | finished = 0; 137 | while (!finished) { 138 | finished = 1; 139 | /* Sample the cache and write to file */ 140 | for (i = 0; i < (argc - 1); i++) { 141 | kill(cpid_arr[i], SIGSTOP); 142 | } 143 | dumpcache(); 144 | dump_proc_map(cpid_arr, argc); 145 | for (i = 0; i < (argc - 1); i++) { 146 | kill(cpid_arr[i], SIGCONT); 147 | } 148 | /* if wstatus isn't modified by waitpid(), WIFEXITED() will 149 | report an incorrect value. This corrects that behavior */ 150 | wstatus = 1; 151 | 152 | /* Check if all experiment processes have completed */ 153 | for (i = 0; i < (argc - 1); i++) { 154 | waitpid(cpid_arr[i], &wstatus, WNOHANG); 155 | if (!WIFEXITED(wstatus)) { 156 | finished = 0; 157 | } 158 | } 159 | if (!finished) { 160 | usleep(MILLISECONDS_BETWEEN_SAMPLES); 161 | } 162 | } 163 | 164 | printf("done! PID of run_experiment is %d\n", getpid()); 165 | printf("done!\n"); 166 | printf("pids:\n"); 167 | FILE *fp; 168 | fp = fopen("/tmp/dumpcache/pids.txt", "w"); 169 | // Write number of iterations 170 | fprintf(fp, "%d\n", round_index); 171 | // Write observer PID to pids.txt 172 | fprintf(fp, "%d\n", getpid()); 173 | for (i=0;i<(argc - 1);i++){ 174 | fprintf(fp, "%d\n", cpid_arr[i]); 175 | printf("[%d]\n", cpid_arr[i]); 176 | } 177 | fclose(fp); 178 | return 0; 179 | } 180 | -------------------------------------------------------------------------------- /experiments/legacy/syn_multiple.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Filename: syn_multiple.c 3 | * 4 | * Author: Dharmesh Tarapore 5 | * 6 | * Description: Run synthetic benchmarks explicitly on 7 | * multiple cores to observe cache contention. 8 | */ 9 | #define _GNU_SOURCE 10 | #include "params.h" 11 | 12 | #define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \ 13 | } while (0) 14 | 15 | 16 | int round_index; 17 | 18 | int read_cache_to_file(char* filename) { 19 | 20 | int outfile; 21 | int dumpcache_fd; 22 | int bytes_to_write = 0; 23 | struct cache_set* cache_contents; 24 | 25 | char csv_file_buf[WRITE_SIZE + 2*CSV_LINE_SIZE]; 26 | int cache_set_idx, cache_line_idx; 27 | 28 | cache_contents = NULL; 29 | 30 | if (((outfile = open(filename, O_CREAT | O_WRONLY | O_SYNC | O_TRUNC, 0666)) < 0)) { 31 | perror("Failed to open outfile"); 32 | exit(1); 33 | } 34 | 35 | if (!cache_contents) { 36 | cache_contents = (struct cache_set*) malloc(sizeof(struct cache_set) * NUM_CACHESETS); 37 | } 38 | 39 | if (((dumpcache_fd = open(PROC_FILENAME, O_RDONLY)) < 0)) { 40 | perror("Failed to open proc file"); 41 | exit(1); 42 | } 43 | 44 | if (read(dumpcache_fd, cache_contents, sizeof(struct cache_set) * NUM_CACHESETS) < 0) { 45 | perror("Failed to read from proc file"); 46 | exit(1); 47 | } 48 | 49 | for (cache_set_idx = 0; cache_set_idx < NUM_CACHESETS; cache_set_idx++) { 50 | for (cache_line_idx = 0; cache_line_idx < NUM_CACHELINES; cache_line_idx++) { 51 | 52 | int d = sprintf(csv_file_buf + bytes_to_write, "%05d,0x%012lx\n", 53 | cache_contents[cache_set_idx].cachelines[cache_line_idx].pid, 54 | cache_contents[cache_set_idx].cachelines[cache_line_idx].addr); 55 | //printf("d is: %d\r\n", d); 56 | bytes_to_write += d; 57 | //bytes_to_write += CSV_LINE_SIZE; 58 | if (bytes_to_write >= WRITE_SIZE){ 59 | //printf("On line 84, bytes_to_write is %d\r\n", bytes_to_write); 60 | if (write(outfile, csv_file_buf, bytes_to_write) == -1) { 61 | perror("Failed to write to outfile"); 62 | } 63 | bytes_to_write = 0; 64 | } 65 | } 66 | } 67 | 68 | if (bytes_to_write){ 69 | // Write remaining amount 70 | //printf("%s%d\n", "Bytes to write:", bytes_to_write); 71 | if (write(outfile, csv_file_buf, bytes_to_write) == -1) { 72 | perror("Failed to write to outfile"); 73 | } 74 | } 75 | 76 | close(outfile); 77 | close(dumpcache_fd); 78 | return 0; 79 | } 80 | 81 | void dump_proc_map(pid_t* proc_ids, int argc) { 82 | int i = 0; 83 | char cbuf[100]; 84 | for(i = 0; i < argc -1; i++) { 85 | sprintf(cbuf, "cat /proc/%d/maps > /tmp/dumpcache/%d-%d.txt", proc_ids[i], proc_ids[i], round_index); 86 | system(cbuf); 87 | } 88 | } 89 | 90 | 91 | void dumpcache(void) { 92 | char filename[100]; 93 | 94 | if (round_index == 0) { 95 | // Make folder to be used as scratch space on first iteration 96 | struct stat st = {0}; 97 | if (stat(SCRATCHSPACE_DIR, &st) == -1) { 98 | mkdir(SCRATCHSPACE_DIR, 0700); 99 | } 100 | } 101 | printf("Dumping cache to CSV files (round: %d)\n", round_index); 102 | fflush(stdout); 103 | 104 | sprintf(filename, "%s/cachedump%d.csv", SCRATCHSPACE_DIR, round_index++); 105 | read_cache_to_file(filename); 106 | } 107 | 108 | 109 | /* 110 | * Goal: 111 | * Obtain a list of at least 2 at most 3 executables 112 | * For each obtained executable: replicate fork process 113 | * in single benchmark driver, while scheduling explicitly on different cores 114 | * having scheduled and executed our benchmarks, snapshot cache as usual 115 | */ 116 | int main(int argc, char* argv[]) { 117 | 118 | char command[100]; 119 | char* executable; 120 | int finished; 121 | int i; 122 | int wstatus; 123 | pid_t cpid; 124 | pid_t* cpid_arr; 125 | 126 | cpu_set_t set; 127 | int parentCPU, childCPU; 128 | 129 | 130 | if (argc < 3) { 131 | printf("Please enter a list of at least 2 executables to run while sampling the cache.\n"); 132 | exit(EXIT_FAILURE); 133 | } 134 | 135 | 136 | /* Temporarily hardcoded. Eventually use argv to dispatch. */ 137 | parentCPU = 0; 138 | childCPU = 1; 139 | 140 | cpid_arr = malloc(sizeof(pid_t) * (argc - 1)); 141 | 142 | CPU_ZERO(&set); 143 | 144 | for (i = 0; i < (argc - 1); i++) { 145 | 146 | executable = argv[i + 1]; /* Executables start at argc[1] */ 147 | cpid = fork(); 148 | if (cpid == -1) { 149 | perror("fork"); 150 | exit(EXIT_FAILURE); 151 | } 152 | if (cpid == 0) { /* Code executed by child */ 153 | CPU_SET(childCPU, &set); 154 | if(sched_setaffinity(getpid(), sizeof(set), &set) == -1) { 155 | errExit("sched_setaffinity"); 156 | } 157 | // For subsequent executables, use the next core 158 | childCPU += 1; 159 | /* guard just in case...though I have to say, 160 | I love writing programs I know only I am going to use! 161 | ...I think I may need to go to bed soon...I'm rambling too much again. 162 | */ 163 | if(childCPU > 3) { 164 | childCPU = 1; 165 | } 166 | sprintf(command, "./%s", executable); 167 | execl(command, command, NULL); 168 | exit(EXIT_FAILURE); 169 | } 170 | else { /* Code executed by parent */ 171 | CPU_SET(parentCPU, &set); 172 | if(sched_setaffinity(getpid(), sizeof(set), &set) == -1) { 173 | errExit("sched_setaffinity"); 174 | } 175 | cpid_arr[i] = cpid; 176 | } 177 | } 178 | 179 | finished = 0; 180 | while (!finished) { 181 | finished = 1; 182 | /* Sample the cache and write to file */ 183 | for (i = 0; i < (argc - 1); i++) { 184 | kill(cpid_arr[i], SIGSTOP); 185 | } 186 | dumpcache(); 187 | dump_proc_map(cpid_arr, argc); 188 | for (i = 0; i < (argc - 1); i++) { 189 | kill(cpid_arr[i], SIGCONT); 190 | } 191 | /* if wstatus isn't modified by waitpid(), WIFEXITED() will 192 | report an incorrect value. This corrects that behavior */ 193 | wstatus = 1; 194 | 195 | /* Check if all experiment processes have completed */ 196 | for (i = 0; i < (argc - 1); i++) { 197 | waitpid(cpid_arr[i], &wstatus, WNOHANG); 198 | if (!WIFEXITED(wstatus)) { 199 | finished = 0; 200 | } 201 | } 202 | if (!finished) { 203 | usleep(MILLISECONDS_BETWEEN_SAMPLES); 204 | } 205 | } 206 | 207 | printf("done! PID of run_experiment is %d\n", getpid()); 208 | printf("done!\n"); 209 | printf("pids:\n"); 210 | FILE *fp; 211 | fp = fopen("/tmp/dumpcache/pids.txt", "w"); 212 | // Write number of iterations 213 | fprintf(fp, "%d\n", round_index); 214 | // Write observer PID to pids.txt 215 | fprintf(fp, "%d\n", getpid()); 216 | for (i=0;i<(argc - 1);i++){ 217 | fprintf(fp, "%d\n", cpid_arr[i]); 218 | printf("[%d]\n", cpid_arr[i]); 219 | } 220 | fclose(fp); 221 | return 0; 222 | } 223 | -------------------------------------------------------------------------------- /experiments/legacy/run_sd_vbs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Goal: run SD-VBS using automated script 3 | * chdir into sd-vbs-bin dir 4 | * For each dir in sd-vbs-bin: 5 | * - chdir into data dir 6 | * - for each dir in the data dir: 7 | * - for each iteration in NUM_RUNS: 8 | * - Port to C and execute res=$(./$b . | grep Cycles | awk '{print $4}') 9 | * - echo "$b; $d; $i; $res" 10 | * - Get PID 11 | * - Get proc map 12 | * - Dump PID and proc map to /tmp/dumpcache 13 | */ 14 | 15 | /* 16 | * Single core: {disparity: [cif]} 17 | * Single core 2: {disparity: [vga]} 18 | * 19 | * Multiple benchmarks single core 20 | * Single core: {disparity: [vga], mser: [vga], sift: [cif], tracking: [cif]} (default scheduler / SCHED_OTHER) 21 | * Single core: {disparity: [vga], mser: [vga], sift: [cif], tracking: [cif]} (SCHED_FIFO) 22 | * 23 | * ----------------------------- Multi core --------------------------------------------------- 24 | * Multi core: {disparity: [vga], mser: [vga]} 25 | * Multi core: {disparity: [vga], mser: [vga], sift: [cif], tracking: [cif]} (SCHED_FIFO) 26 | * 27 | * [Time by commenting out dumpcache and dump proc maps and time with both active to estimate overhead] 28 | */ 29 | 30 | #include "params.h" 31 | #include 32 | #include 33 | 34 | /* 35 | * TODO: stat directories eventually to avoid hardcoding which benchmarks to run. 36 | * I am leaving these as a list since it's easier to comment out benchmarks I 37 | * don't want to see than move folders around. 38 | */ 39 | static const char *BASE_SD_VBS_PATH = "./sd-vbs-bin/"; 40 | static const char *list_of_benchmarks[] = {"disparity", "mser", "sift", "tracking"}; 41 | //{"disparity", "localization", "mser", "sift","texture_synthesis", "tracking"}; 42 | 43 | static const short NUM_SD_VBS_BENCHMARKS = 4; 44 | /* Set to 7 to run all */ 45 | static const short NUM_SD_VBS_BENCHMARKS_DATASETS = 1; 46 | 47 | /* In increasing order of CPU cycle consumption: 48 | * http://cseweb.ucsd.edu/~mbtaylor/vision/release/SD-VBS.pdf 49 | */ 50 | static const char *list_of_datasets[] = {"vga", "cif"};//{"cif", "qcif", "sqcif", "sim", "sim_fast", "fullhd", "vga"}; 51 | 52 | /* Originally 30 in the bash script. */ 53 | static const short NUM_SD_VBS_BENCHMARKS_ITERS = 1; 54 | 55 | static short NUM_TOTAL_EXECUTABLES = 4; //NUM_SD_VBS_BENCHMARKS * NUM_SD_VBS_BENCHMARKS_DATASETS; 56 | 57 | int round_index; 58 | 59 | int read_cache_to_file(char* filename) { 60 | 61 | int outfile; 62 | int dumpcache_fd; 63 | int bytes_to_write = 0; 64 | struct cache_set* cache_contents; 65 | 66 | char csv_file_buf[WRITE_SIZE + 2*CSV_LINE_SIZE]; 67 | int cache_set_idx, cache_line_idx; 68 | 69 | cache_contents = NULL; 70 | 71 | if (((outfile = open(filename, O_CREAT | O_WRONLY | O_SYNC | O_TRUNC, 0666)) < 0)) { 72 | perror("Failed to open outfile"); 73 | exit(1); 74 | } 75 | 76 | if (!cache_contents) { 77 | cache_contents = (struct cache_set*) malloc(sizeof(struct cache_set) * NUM_CACHESETS); 78 | } 79 | 80 | if (((dumpcache_fd = open(PROC_FILENAME, O_RDONLY)) < 0)) { 81 | perror("Failed to open proc file"); 82 | exit(1); 83 | } 84 | 85 | if (read(dumpcache_fd, cache_contents, sizeof(struct cache_set) * NUM_CACHESETS) < 0) { 86 | perror("Failed to read from proc file"); 87 | exit(1); 88 | } 89 | 90 | for (cache_set_idx = 0; cache_set_idx < NUM_CACHESETS; cache_set_idx++) { 91 | for (cache_line_idx = 0; cache_line_idx < NUM_CACHELINES; cache_line_idx++) { 92 | 93 | int d = sprintf(csv_file_buf + bytes_to_write, "%05d,0x%012lx\n", 94 | cache_contents[cache_set_idx].cachelines[cache_line_idx].pid, 95 | cache_contents[cache_set_idx].cachelines[cache_line_idx].addr); 96 | //printf("d is: %d\r\n", d); 97 | bytes_to_write += d; 98 | //bytes_to_write += CSV_LINE_SIZE; 99 | if (bytes_to_write >= WRITE_SIZE){ 100 | //printf("On line 84, bytes_to_write is %d\r\n", bytes_to_write); 101 | if (write(outfile, csv_file_buf, bytes_to_write) == -1) { 102 | perror("Failed to write to outfile"); 103 | } 104 | bytes_to_write = 0; 105 | } 106 | } 107 | } 108 | 109 | if (bytes_to_write){ 110 | // Write remaining amount 111 | printf("%s%d\n", "Bytes to write:", bytes_to_write); 112 | if (write(outfile, csv_file_buf, bytes_to_write) == -1) { 113 | perror("Failed to write to outfile"); 114 | } 115 | } 116 | 117 | close(outfile); 118 | close(dumpcache_fd); 119 | return 0; 120 | } 121 | 122 | void dump_proc_map(pid_t* proc_ids, int argc) { 123 | printf("Dumping: %d\r\n", argc); 124 | int i = 0; 125 | char cbuf[100]; 126 | for(i = 0; i < argc; i++) { 127 | sprintf(cbuf, "cat /proc/%d/maps > /tmp/dumpcache/%d-%d.txt", proc_ids[i], proc_ids[i], round_index); 128 | system(cbuf); 129 | } 130 | } 131 | 132 | void dumpcache(void) { 133 | char filename[100]; 134 | 135 | if (round_index == 0) { 136 | // Make folder to be used as scratch space on first iteration 137 | struct stat st = {0}; 138 | if (stat(SCRATCHSPACE_DIR, &st) == -1) { 139 | mkdir(SCRATCHSPACE_DIR, 0700); 140 | } 141 | } 142 | printf("Dumping cache to CSV files (round: %d)\n", round_index); 143 | fflush(stdout); 144 | 145 | sprintf(filename, "%s/cachedump%d.csv", SCRATCHSPACE_DIR, round_index++); 146 | read_cache_to_file(filename); 147 | } 148 | 149 | int main(int argc, char* argv[]) { 150 | 151 | char command[200]; 152 | char cbuf[100]; 153 | int finished; 154 | short i = 0; 155 | short j = 0; 156 | pid_t cpid; 157 | short ctr = 0; 158 | pid_t* cpid_arr; 159 | char* executable; 160 | int wstatus; 161 | 162 | 163 | cpid_arr = malloc(sizeof(pid_t) * NUM_TOTAL_EXECUTABLES); 164 | 165 | // Enumerate all benchmark paths 166 | for(i = 0; i < NUM_SD_VBS_BENCHMARKS; i++) { 167 | for(j = 0; j < NUM_SD_VBS_BENCHMARKS_DATASETS; j++) { 168 | 169 | // If i is 0 or 1 (disparity or mser, run vga) 170 | // If i is 2 or 3 (sift or tracking, run cif) 171 | 172 | if(i == 0 || i == 1) { 173 | sprintf(command, "%s%s/data/%s/%s", 174 | BASE_SD_VBS_PATH, list_of_benchmarks[i], list_of_datasets[0], list_of_benchmarks[i]); 175 | } else if(i == 2 || i == 3) { 176 | sprintf(command, "%s%s/data/%s/%s", 177 | BASE_SD_VBS_PATH, list_of_benchmarks[i], list_of_datasets[1], list_of_benchmarks[i]); 178 | } 179 | 180 | // sprintf(command, "%s%s/data/%s/%s", 181 | // BASE_SD_VBS_PATH, list_of_benchmarks[i], list_of_datasets[j], list_of_benchmarks[i]); 182 | 183 | printf("Running: %s\r\n", command); 184 | 185 | // For the current proc - set priority 186 | struct sched_param sp = { .sched_priority = 99 }; 187 | int ret; 188 | ret = sched_setscheduler(0, SCHED_FIFO, &sp); 189 | sp.sched_priority = 98; 190 | if (ret == -1) { 191 | perror("sched_setscheduler"); 192 | return 1; 193 | } 194 | 195 | cpid = fork(); 196 | if (cpid == -1) { 197 | perror("fork"); 198 | exit(EXIT_FAILURE); 199 | } 200 | if (cpid == 0) { 201 | // For subsequent procs, decrement priority 202 | sp.sched_priority -= 1; 203 | ret = sched_setscheduler(getpid(), SCHED_FIFO, &sp); 204 | if(i == 0 || i == 1) {sprintf(cbuf, "%s%s/data/%s/", BASE_SD_VBS_PATH, 205 | list_of_benchmarks[i], list_of_datasets[0]);} 206 | else if(i == 2 || i == 3) {sprintf(cbuf, "%s%s/data/%s/", BASE_SD_VBS_PATH, 207 | list_of_benchmarks[i], list_of_datasets[1]);} 208 | 209 | // sprintf(cbuf, "%s%s/data/%s/", BASE_SD_VBS_PATH, 210 | // list_of_benchmarks[i], list_of_datasets[j]); 211 | execl(command, command, cbuf, (char *)0); 212 | exit(EXIT_FAILURE); 213 | } 214 | else { 215 | cpid_arr[i*NUM_SD_VBS_BENCHMARKS_DATASETS+j] = cpid; 216 | } 217 | 218 | } 219 | } 220 | 221 | 222 | finished = 0; 223 | while (!finished) { 224 | finished = 1; 225 | /* Sample the cache and write to file */ 226 | for (ctr = 0; ctr < NUM_TOTAL_EXECUTABLES; ctr++) { 227 | kill(cpid_arr[ctr], SIGSTOP); 228 | } 229 | dumpcache(); 230 | dump_proc_map(cpid_arr, NUM_TOTAL_EXECUTABLES); 231 | for (ctr = 0; ctr < NUM_TOTAL_EXECUTABLES; ctr++) { 232 | kill(cpid_arr[ctr], SIGCONT); 233 | } 234 | /* if wstatus isn't modified by waitpid(), WIFEXITED() will 235 | report an incorrect value. This corrects that behavior */ 236 | wstatus = 1; 237 | 238 | /* Check if all experiment processes have completed */ 239 | for (ctr = 0; ctr < NUM_TOTAL_EXECUTABLES; ctr++) { 240 | waitpid(cpid_arr[ctr], &wstatus, WNOHANG); 241 | if(!WIFEXITED(wstatus)) { 242 | finished = 0; 243 | } 244 | } 245 | if(!finished) { 246 | usleep(MILLISECONDS_BETWEEN_SAMPLES); 247 | } 248 | } 249 | 250 | 251 | printf("done! PID of run_experiment is %d\n", getpid()); 252 | printf("done!\n"); 253 | printf("pids:\n"); 254 | FILE *fp; 255 | fp = fopen("/tmp/dumpcache/pids.txt", "w"); 256 | // Write number of iterations 257 | fprintf(fp, "%d\n", round_index); 258 | // Write observer PID to pids.txt 259 | fprintf(fp, "%d\n", getpid()); 260 | for (ctr=0;ctr= r.start and page < r.end + 4096: 75 | self.page_to_regions[page] = i 76 | self.regions_to_pages[i].append(page) 77 | break 78 | 79 | # Sanity check here 80 | if page not in self.page_to_regions: 81 | self.not_in_regions += 1 82 | 83 | def get_page_ranges(self): 84 | if self.ranges != None: 85 | return self.ranges 86 | 87 | self.ranges = [] 88 | sorted_addr = sorted(self.pages.items(), key=operator.itemgetter(0), reverse=False) 89 | start = None 90 | end = None 91 | for p in sorted_addr: 92 | if start == None: 93 | start = p[0] 94 | end = p[0] 95 | continue 96 | 97 | if p[0] == (end + PAGE_SIZE): 98 | end = p[0] 99 | else: 100 | self.ranges.append((start, end)) 101 | start = p[0] 102 | end = p[0] 103 | 104 | if start != None: 105 | self.ranges.append((start, end)) 106 | 107 | return self.ranges 108 | 109 | class CacheDump: 110 | def __init__(self, other_pids, dump_file): 111 | self.dump_file = dump_file 112 | self.pid_accesses = {} 113 | self.pid_regions = {} 114 | self.bad_entries = 0 115 | self.unresolved = 0 116 | self.okay_entries = 0 117 | self.tot_entries = 0 118 | self.not_in_pidfile = 0 119 | self.pids_from_file = other_pids 120 | 121 | # Make sure we have an entry for all the PIDs on file 122 | for pid in self.pids_from_file: 123 | if pid not in self.pid_accesses: 124 | self.pid_accesses[pid] = Accesses(pid) 125 | 126 | # Parse cache dump file 127 | file = open(self.dump_file) 128 | lines = [l.strip('\n') for l in file.readlines()] 129 | for l in lines: 130 | fields = l.split(",") 131 | if len(fields) == 2: 132 | (pid, page) = fields 133 | pid = int(pid) 134 | 135 | # Make sure we have an entry for each new PID we 136 | # encounter 137 | if pid not in self.pid_accesses: 138 | self.pid_accesses[pid] = Accesses(pid) 139 | 140 | if pid in self.pids_from_file: 141 | self.pid_accesses[pid].add_access(page) 142 | self.okay_entries += 1 143 | elif pid >= 0: 144 | self.pid_accesses[pid].add_access(page) 145 | self.okay_entries += 1 146 | self.not_in_pidfile += 1 147 | else: 148 | self.unresolved += 1 149 | else: 150 | self.bad_entries += 1 151 | self.tot_entries += 1 152 | 153 | file.close() 154 | 155 | # Automatically try to find proc/pid/maps file for each PID 156 | base_path = dump_file.split("/")[0:-1] 157 | base_path = ("/").join(base_path) + "/" 158 | dump_id = dump_file.split("/")[-1].split(".") 159 | dump_id = (dump_id[0])[9:] 160 | 161 | for pid in self.pid_accesses: 162 | proc_file = base_path + str(pid) + "-" + str(dump_id) + ".txt" 163 | if os.path.isfile(proc_file): 164 | self.pid_regions[pid] = self.__parse_maps(proc_file) 165 | self.pid_accesses[pid].match_to_regions(self.pid_regions[pid]) 166 | 167 | # Internal function to parse proc/pid/maps files 168 | def __parse_maps(self, proc_file): 169 | maps = [] 170 | 171 | proc_file = open(proc_file) 172 | lines = [l.strip('\n') for l in proc_file.readlines()] 173 | 174 | for i in range(len(lines)): 175 | fields = lines[i].split (" ", 5) 176 | 177 | start_end = None 178 | perm = None 179 | off = None 180 | dev = None 181 | inode = None 182 | path = None 183 | 184 | if len (fields) == 6: 185 | (start_end, perm, off, dev, inode, path) = fields 186 | else: 187 | (start_end, perm, off, dev, inode) = fields 188 | path = "" 189 | 190 | (start, end) = start_end.split("-") 191 | 192 | region = Region(start, end, perm, off, dev, inode, path, i) 193 | maps.append(region) 194 | 195 | return maps 196 | 197 | def print_stats(self): 198 | print "\nTotal entries: \t%i\nBad: \t\t%i (%0.2f %%)\nNo PID: \t%i (%0.2f %%)\nGood: \t\t%i (%0.2f %%)\n" \ 199 | % (self.tot_entries, \ 200 | self.bad_entries, 100*float(self.bad_entries)/self.tot_entries, \ 201 | self.unresolved, 100*float(self.unresolved)/self.tot_entries, \ 202 | self.okay_entries, 100*float(self.okay_entries)/self.tot_entries, \ 203 | ) 204 | 205 | for p in self.pid_accesses: 206 | print "\nMost Accessed pages for PID %i:" % (p) 207 | ranked = self.pid_accesses[p].get_ranked_list(10) 208 | for page in ranked: 209 | print "Page: %s Lines: %i" % (hex(page[0]), page[1]) 210 | 211 | ranges = self.pid_accesses[p].get_page_ranges() 212 | for r in ranges: 213 | print "[%s - %s] (%i pages)" % (hex(r[0]), hex(r[1]), (r[1] - r[0])/PAGE_SIZE + 1) 214 | 215 | class Region: 216 | def __init__(self, start, end, perm, off, dev, inode, path, index): 217 | self.start = int(start, 16) 218 | self.end = int(end, 16) 219 | self.perm = perm 220 | self.off = off 221 | self.dev = dev 222 | self.inode = inode 223 | self.path = path.strip() 224 | self.index = index 225 | 226 | # Determine the type 227 | self.is_heap = False 228 | self.is_stack = False 229 | self.is_vvar = False 230 | self.is_vdso = False 231 | self.is_vsyscall = False 232 | self.is_mapped = False 233 | self.is_anon = False 234 | self.short_name = "" 235 | 236 | if (self.path == "[heap]"): 237 | self.is_heap = True 238 | self.anon = True 239 | 240 | elif (self.path == "[stack]"): 241 | self.is_stack = True 242 | self.anon = True 243 | 244 | elif (self.path == "[vvar]"): 245 | self.is_vvar = True 246 | self.anon = True 247 | 248 | elif (self.path == "[vdso]"): 249 | self.is_vdso = True 250 | self.anon = True 251 | 252 | elif (self.path == "[vsyscall]"): 253 | self.is_vsyscall = True 254 | self.anon = True 255 | 256 | elif (self.path != ""): 257 | self.is_mapped = True 258 | last = self.path.split("/")[-1] 259 | if self.index == 0 and self.perm == "r-xp": 260 | self.short_name = "[text]" 261 | elif self.index in [1, 2]: 262 | if self.perm == "r--p": 263 | self.short_name = "[rodata]" 264 | elif self.perm == "rw-p": 265 | self.short_name = "[data]" 266 | else: 267 | self.short_name = last.split("-")[0] 268 | 269 | else: 270 | self.is_anon = True 271 | self.path = "[anon]" 272 | 273 | if self.short_name == "": 274 | self.short_name = self.path 275 | 276 | def __repr__(self): 277 | return "[%s] %s->%s (%s)" % (self.index, self.start, self.end, self.path) 278 | 279 | 280 | class MemSpectrum: 281 | def __init__(self, pid, pid_file, start_idx, stop_idx = 0): 282 | self.pid = pid 283 | self.pid_file = pid_file 284 | self.start_idx = start_idx 285 | self.stop_idx = stop_idx 286 | 287 | # Find out path from pid file 288 | self.base_path = pid_file.split("/")[0:-1] 289 | self.base_path = ("/").join(self.base_path) + "/" 290 | 291 | # Parse other interesting pids for this experiment 292 | self.other_pids = [] 293 | 294 | # Parse PID file 295 | file = open(self.pid_file) 296 | lines = [l.strip('\n') for l in file.readlines()] 297 | 298 | # Auto-adjust number of iterations if not passed explicitly by the user 299 | self.total_iter = int(lines[0]) 300 | if self.stop_idx == 0: 301 | self.stop_idx = self.total_iter 302 | 303 | for l in lines[1:]: 304 | pid = int(l) 305 | self.other_pids.append(pid) 306 | 307 | file.close() 308 | 309 | # Parse all the cachedump files 310 | self.dumps = [] 311 | for i in range(start_idx, stop_idx+1): 312 | dumpfile = self.base_path + "cachedump" + str(i) + ".csv" 313 | 314 | print "Parsing file: %s" % (dumpfile) 315 | 316 | if os.path.isfile(dumpfile): 317 | dump = CacheDump(self.other_pids, dumpfile) 318 | self.dumps.append(dump) 319 | else: 320 | print "Error: unable to find file: %s" % (dumpfile) 321 | break 322 | 323 | def get_total_blocks_in_region(self, pid, region_idx): 324 | total = 0 325 | for i, d in enumerate(self.dumps): 326 | acc = d.pid_accesses[self.pid] 327 | 328 | if region_idx in acc.regions_to_pages: 329 | total += len(acc.regions_to_pages[region_idx]) 330 | 331 | return total 332 | 333 | def get_range_blocks_in_region(self, pid, region_idx): 334 | gl_max = None 335 | gl_min = None 336 | 337 | for i, d in enumerate(self.dumps): 338 | acc = d.pid_accesses[pid] 339 | 340 | if region_idx not in acc.regions_to_pages: 341 | continue 342 | 343 | acc_reg = acc.regions_to_pages[region_idx] 344 | if (len(acc_reg) == 0): 345 | continue 346 | 347 | cur_min = min(acc_reg) 348 | cur_max = max(acc_reg) 349 | 350 | if gl_max == None or gl_max < cur_max: 351 | gl_max = cur_max 352 | 353 | if gl_min == None or gl_min > cur_min: 354 | gl_min = cur_min 355 | 356 | if gl_max == None: 357 | return 0 358 | 359 | return (gl_max - gl_min) 360 | 361 | def get_stats_per_region(self, pid = None): 362 | if pid == None: 363 | pid = self.pid 364 | 365 | for i, d in enumerate(self.dumps): 366 | print "=== DUMP %i ===" % (i) 367 | acc = d.pid_accesses[pid] 368 | reg = d.pid_regions[pid] 369 | 370 | for j, r in enumerate(reg): 371 | print "Area [%i] (%s) - Pages: %i" % (j, reg[j].short_name, \ 372 | len(acc.regions_to_pages[j])) 373 | 374 | if __name__ == '__main__': 375 | 376 | if len(sys.argv) > 1: 377 | spect = MemSpectrum(8813, sys.argv[1], 20, 25) 378 | 379 | spect.get_stats_per_region() 380 | 381 | else: 382 | print(sys.argv[0] + " /proc//maps") 383 | 384 | 385 | -------------------------------------------------------------------------------- /dumpcache.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | /* Global Defines */ 19 | #define CACHESETS_TO_WRITE 2048 20 | #define L2_SIZE 2*1024*1024 21 | #define MODNAME "dumpcache" 22 | #define WAYS 16 23 | 24 | /* Command to access the configuration interface */ 25 | #define DUMPCACHE_CMD_CONFIG _IOW(0, 0, unsigned long) 26 | /* Command to initiate a cache dump */ 27 | #define DUMPCACHE_CMD_SNAPSHOT _IOW(0, 1, unsigned long) 28 | 29 | #define FULL_ADDRESS 0 30 | 31 | #pragma GCC push_options 32 | #pragma GCC optimize ("O0") 33 | // Might need this 34 | //pragma GCC pop_options 35 | /* Struct representing a single cache line - each cacheline struct is 68 bytes */ 36 | struct cache_line 37 | { 38 | pid_t pid; 39 | uint64_t addr; 40 | }; 41 | 42 | struct cache_set 43 | { 44 | struct cache_line cachelines[16]; 45 | }; 46 | 47 | struct cache_sample { 48 | struct cache_set sets[CACHESETS_TO_WRITE]; 49 | }; 50 | 51 | /* Global variables */ 52 | 53 | /* Unfortunately this platform has two apettures for DRAM, with a 54 | * large hole in the middle. Here is what the address space loops like 55 | * when the kernel is booted with mem=2560 (2.5 GB). 56 | * 57 | * 0x080000000 -> 0x0fedfffff: Normal memory (aperture 1) 58 | * 0x0fee00000 -> 0x0ffffffff: Cache buffer, part 1, size = 0x1200000 (aperture 1) 59 | * 0x100000000 -> 0x1211fffff: Normal memory (aperture 2) 60 | * 0x121200000 -> 0x17fffffff: Cache buffer, part 2, size = 0x5ee00000 (aperture 2) 61 | */ 62 | 63 | /* This vaiable is to keep track of the current buffer in use by the 64 | * module. It must be reset explicitly to prevent overwriting existing 65 | * data. */ 66 | 67 | #define CACHE_BUF_BASE1 0x0fee00000UL 68 | #define CACHE_BUF_BASE2 0x121200000UL 69 | 70 | #define CACHE_BUF_END1 0x0fee00000UL 71 | 72 | //#define CACHE_BUF_END1 0x100000000UL 73 | #define CACHE_BUF_END2 0x180000000UL 74 | 75 | #define CACHE_BUF_SIZE1 (CACHE_BUF_END1 - CACHE_BUF_BASE1) 76 | #define CACHE_BUF_SIZE2 (CACHE_BUF_END2 - CACHE_BUF_BASE2) 77 | 78 | #define CACHE_BUF_COUNT1 (CACHE_BUF_SIZE1 / sizeof(struct cache_sample)) 79 | #define CACHE_BUF_COUNT2 (CACHE_BUF_SIZE2 / sizeof(struct cache_sample)) 80 | 81 | #define DUMPCACHE_CMD_VALUE_WIDTH 16 82 | #define DUMPCACHE_CMD_VALUE_MASK ((1 << DUMPCACHE_CMD_VALUE_WIDTH) - 1) 83 | #define DUMPCACHE_CMD_VALUE(cmd) \ 84 | (cmd & DUMPCACHE_CMD_VALUE_MASK) 85 | 86 | /* Command to set the current buffer number */ 87 | #define DUMPCACHE_CMD_SETBUF_SHIFT (1 << (DUMPCACHE_CMD_VALUE_WIDTH + 1)) 88 | 89 | /* Command to retrievet the current buffer number */ 90 | #define DUMPCACHE_CMD_GETBUF_SHIFT (1 << (DUMPCACHE_CMD_VALUE_WIDTH + 2)) 91 | 92 | /* Command to enable/disable buffer autoincrement */ 93 | #define DUMPCACHE_CMD_AUTOINC_EN_SHIFT (1 << (DUMPCACHE_CMD_VALUE_WIDTH + 3)) 94 | #define DUMPCACHE_CMD_AUTOINC_DIS_SHIFT (1 << (DUMPCACHE_CMD_VALUE_WIDTH + 4)) 95 | 96 | /* Command to enable/disable address resolution */ 97 | #define DUMPCACHE_CMD_RESOLVE_EN_SHIFT (1 << (DUMPCACHE_CMD_VALUE_WIDTH + 5)) 98 | #define DUMPCACHE_CMD_RESOLVE_DIS_SHIFT (1 << (DUMPCACHE_CMD_VALUE_WIDTH + 6)) 99 | 100 | /* Command to enable/disable snapshot timestamping */ 101 | #define DUMPCACHE_CMD_TIMESTAMP_EN_SHIFT (1 << (DUMPCACHE_CMD_VALUE_WIDTH + 7)) 102 | #define DUMPCACHE_CMD_TIMESTAMP_DIS_SHIFT (1 << (DUMPCACHE_CMD_VALUE_WIDTH + 8)) 103 | 104 | static uint32_t cur_buf = 0; 105 | static unsigned long flags; 106 | 107 | /* Beginning of cache buffer in aperture 1 */ 108 | static struct cache_sample * __buf_start1 = NULL; 109 | 110 | /* Beginning of cache buffer in aperture 2 */ 111 | static struct cache_sample * __buf_start2 = NULL; 112 | 113 | /* Pointer to buffer currently in use. */ 114 | static struct cache_sample * cur_sample = NULL; 115 | 116 | //static struct vm_area_struct *cache_set_buf_vma; 117 | static int dump_all_indices_done; 118 | 119 | //spinlock_t snap_lock = SPIN_LOCK_UNLOCK; 120 | static DEFINE_SPINLOCK(snap_lock); 121 | 122 | static bool rmap_one_func(struct page *page, struct vm_area_struct *vma, unsigned long addr, void *arg); 123 | static void (*rmap_walk_func) (struct page *page, struct rmap_walk_control *rwc) = NULL; 124 | 125 | /* Function prototypes */ 126 | static int dumpcache_open (struct inode *inode, struct file *filp); 127 | static int dump_index(int index, struct cache_set* buf); 128 | static int dump_all_indices(void); 129 | 130 | static void *c_start(struct seq_file *m, loff_t *pos) 131 | { 132 | return *pos < 1 ? (void *)1 : NULL; 133 | } 134 | 135 | static void *c_next(struct seq_file *m, void *v, loff_t *pos) 136 | { 137 | ++*pos; 138 | return c_start(m, pos); 139 | } 140 | 141 | static void c_stop(struct seq_file *m, void *v) {} 142 | 143 | void cpu_stall (void * info) 144 | { 145 | (void)info; 146 | spin_lock(&snap_lock); 147 | spin_unlock(&snap_lock); 148 | } 149 | 150 | static int c_show(struct seq_file *m, void *v) 151 | { 152 | 153 | /* Make sure that the buffer has the right size */ 154 | m->size = sizeof(struct cache_sample) + 32; 155 | m->buf = kvmalloc(sizeof(struct cache_sample) + 32, GFP_KERNEL);; 156 | 157 | /* Read buffer into sequential file interface */ 158 | if (seq_write(m, cur_sample, sizeof(struct cache_sample)) != 0) { 159 | pr_info("Seq write returned non-zero value\n"); 160 | } 161 | 162 | return 0; 163 | } 164 | 165 | /* This function returns a pointr to the ind-th sample in the 166 | * buffer. */ 167 | static inline struct cache_sample * sample_from_index(uint32_t ind) 168 | { 169 | if (ind < CACHE_BUF_COUNT1) 170 | return &__buf_start1[ind]; 171 | 172 | else if (ind < CACHE_BUF_COUNT1 + CACHE_BUF_COUNT2) 173 | return &__buf_start2[ind - CACHE_BUF_COUNT1]; 174 | 175 | else 176 | return NULL; 177 | } 178 | 179 | static int acquire_snapshot(void) 180 | { 181 | int processor_id; 182 | struct cpumask cpu_mask; 183 | 184 | /* Prepare cpu mask with all CPUs except current one */ 185 | processor_id = get_cpu(); 186 | cpumask_copy(&cpu_mask, cpu_online_mask); 187 | cpumask_clear_cpu(processor_id, &cpu_mask); //processor_id, &cpu_mask); 188 | 189 | /* Acquire lock to spin other CPUs */ 190 | spin_lock(&snap_lock); 191 | preempt_disable(); 192 | 193 | /* Critical section! */ 194 | on_each_cpu_mask(&cpu_mask, cpu_stall, NULL, 0); 195 | 196 | /* Perform cache snapshot */ 197 | dump_all_indices(); 198 | 199 | preempt_enable(); 200 | spin_unlock(&snap_lock); 201 | put_cpu(); 202 | 203 | /* Figure out if we need to increase the buffer pointer */ 204 | if (flags & DUMPCACHE_CMD_AUTOINC_EN_SHIFT) { 205 | cur_buf += 1; 206 | 207 | if (cur_buf >= CACHE_BUF_COUNT1 + CACHE_BUF_COUNT2) { 208 | cur_buf = 0; 209 | } 210 | 211 | /* Set the pointer to the next available buffer */ 212 | cur_sample = sample_from_index(cur_buf); 213 | } 214 | 215 | return 0; 216 | } 217 | 218 | static int dumpcache_config(unsigned long cmd) 219 | { 220 | /* Set the sample buffer accoridng to what passed from user 221 | * space */ 222 | if(cmd & DUMPCACHE_CMD_SETBUF_SHIFT) { 223 | uint32_t val = DUMPCACHE_CMD_VALUE(cmd); 224 | 225 | if(val >= CACHE_BUF_COUNT1 + CACHE_BUF_COUNT2) 226 | return -ENOMEM; 227 | 228 | cur_buf = val; 229 | cur_sample = sample_from_index(val); 230 | } 231 | 232 | if (cmd & DUMPCACHE_CMD_GETBUF_SHIFT) { 233 | return cur_buf; 234 | } 235 | 236 | if (cmd & DUMPCACHE_CMD_AUTOINC_EN_SHIFT) { 237 | flags |= DUMPCACHE_CMD_AUTOINC_EN_SHIFT; 238 | } else if (cmd & DUMPCACHE_CMD_AUTOINC_DIS_SHIFT) { 239 | flags &= ~DUMPCACHE_CMD_AUTOINC_EN_SHIFT; 240 | } 241 | 242 | if (cmd & DUMPCACHE_CMD_RESOLVE_EN_SHIFT) { 243 | flags |= DUMPCACHE_CMD_RESOLVE_EN_SHIFT; 244 | } else if (cmd & DUMPCACHE_CMD_RESOLVE_DIS_SHIFT) { 245 | flags &= ~DUMPCACHE_CMD_RESOLVE_EN_SHIFT; 246 | } 247 | 248 | return 0; 249 | } 250 | 251 | /* The IOCTL interface of the proc file descriptor is used to pass 252 | * configuration commands */ 253 | static long dumpcache_ioctl(struct file *file, unsigned int ioctl, unsigned long arg) 254 | { 255 | long err; 256 | 257 | switch (ioctl) { 258 | case DUMPCACHE_CMD_CONFIG: 259 | err = dumpcache_config(arg); 260 | break; 261 | 262 | case DUMPCACHE_CMD_SNAPSHOT: 263 | err = acquire_snapshot(); 264 | break; 265 | 266 | default: 267 | pr_err("Invalid command: 0x%08x\n", ioctl); 268 | err = -EINVAL; 269 | break; 270 | } 271 | 272 | return err; 273 | } 274 | 275 | 276 | static const struct seq_operations dumpcache_seq_ops = { 277 | .start = c_start, 278 | .next = c_next, 279 | .stop = c_stop, 280 | .show = c_show 281 | }; 282 | 283 | /* ProcFS entry setup and definitions */ 284 | static const struct file_operations dumpcache_fops = { 285 | .owner = THIS_MODULE, 286 | .unlocked_ioctl = dumpcache_ioctl, 287 | .compat_ioctl = dumpcache_ioctl, 288 | .open = dumpcache_open, 289 | .read = seq_read, 290 | .llseek = seq_lseek, 291 | .release = seq_release 292 | }; 293 | 294 | static inline void asm_flush_cache(void) { 295 | asm volatile( 296 | "MCR p15, 0, r0, c7, c5, 0\t\n" 297 | "MCR p15, 0, Rd, c7, c6, 0" 298 | ); 299 | } 300 | 301 | // Ramindex operation 302 | // 4.3.64 in ARM Cortex-A57 MPCore Processor Technical Reference Manual 303 | static inline void asm_ramindex_mcr(u32 ramindex) 304 | { 305 | asm volatile( 306 | "sys #0, c15, c4, #0, %0\t\n" 307 | "dsb sy\t\n" 308 | "isb" :: "r" (ramindex)); 309 | } 310 | 311 | 312 | // reading from DL1DATA0_EL1 313 | // 4.3.63 in ARM Cortex-A57 MPCore Processor Technical Reference Manual 314 | static inline void asm_ramindex_mrc(u32 *dl1data, u8 sel) 315 | { 316 | if (sel & 0x01) asm volatile("mrs %0,S3_0_c15_c1_0" : "=r"(dl1data[0])); 317 | if (sel & 0x02) asm volatile("mrs %0,S3_0_c15_c1_1" : "=r"(dl1data[1])); 318 | if (sel & 0x04) asm volatile("mrs %0,S3_0_c15_c1_2" : "=r"(dl1data[2])); 319 | if (sel & 0x08) asm volatile("mrs %0,S3_0_c15_c1_3" : "=r"(dl1data[3])); 320 | } 321 | 322 | 323 | // Get Tag of L2 cache entry at (index,way) 324 | // Tag bank select ignored, 2MB L2 cache assumed 325 | static inline void get_tag(u32 index, u32 way, u32 *dl1data) 326 | { 327 | u32 ramid = 0x10; 328 | u32 ramindex = (ramid << 24) + (way << 18) + (index << 6); 329 | asm_ramindex_mcr(ramindex); 330 | asm_ramindex_mrc(dl1data, 0x01); 331 | 332 | // Check if MOESI state is invalid, and if so, zero out the address 333 | if (((*dl1data) & 0x03UL) == 0) { 334 | *dl1data = 0; 335 | return; 336 | } 337 | // Isolate the tag 338 | *dl1data &= ~(0x03UL); 339 | *dl1data <<= 12; 340 | *dl1data |= (index << 5); 341 | } 342 | 343 | bool rmap_one_func(struct page *page, struct vm_area_struct *vma, unsigned long addr, void *arg) 344 | { 345 | 346 | struct mm_struct* mm; 347 | struct task_struct* ts; 348 | struct process_data 349 | { 350 | pid_t pid; 351 | uint64_t addr; 352 | }; 353 | 354 | ((struct process_data*) arg)->addr = 0; 355 | 356 | // Check if mm struct is null 357 | mm = vma->vm_mm; 358 | if (!mm) { 359 | ((struct process_data*) arg)->pid = (pid_t)99999; 360 | return true; 361 | } 362 | 363 | // Check if task struct is null 364 | ts = mm->owner; 365 | if (!ts) { 366 | ((struct process_data*) arg)->pid = (pid_t)99999; 367 | return true; 368 | } 369 | 370 | // If pid is 1, continue searching pages 371 | if ((ts->pid) == 1) { 372 | ((struct process_data*) arg)->pid = (ts->pid); 373 | return true; 374 | } 375 | 376 | // *Probably* the correct pid 377 | ((struct process_data*) arg)->pid = (ts->pid); 378 | ((struct process_data*) arg)->addr = addr; 379 | return false; 380 | } 381 | 382 | int done_func(struct page *page) 383 | { 384 | return 1; 385 | } 386 | 387 | 388 | bool invalid_func(struct vm_area_struct *vma, void *arg) 389 | { 390 | struct process_data 391 | { 392 | pid_t pid; 393 | uint64_t addr; 394 | }; 395 | 396 | ((struct process_data*) arg)->pid = (pid_t)99999; 397 | return false; 398 | } 399 | 400 | static int __dump_index_resolve(int index, struct cache_set* buf) 401 | { 402 | int way; 403 | u32 physical_address; 404 | struct page* derived_page; 405 | struct rmap_walk_control rwc; 406 | struct rmap_walk_control * rwc_p; 407 | 408 | /* This will be used to invoke address resolution */ 409 | struct cache_line process_data_struct; 410 | 411 | // Instantiate rmap walk control struct 412 | rwc.arg = &process_data_struct; 413 | rwc.rmap_one = rmap_one_func; 414 | rwc.done = NULL; //done_func; 415 | rwc.anon_lock = NULL; 416 | rwc.invalid_vma = invalid_func; 417 | rwc_p = &rwc; 418 | 419 | for (way = 0; way < WAYS; way++) { 420 | get_tag(index, way, &physical_address); 421 | if (!physical_address) 422 | continue; 423 | 424 | derived_page = phys_to_page(((u64)physical_address << 1)); 425 | 426 | // Initalize struct 427 | (buf->cachelines[way]).pid = 0; //process_data_struct->pid;// = 0; 428 | (buf->cachelines[way]).addr = ((u64)physical_address << 1); //process_data_struct->addr;// = 0; 429 | 430 | /* Reset address */ 431 | process_data_struct.addr = 0; 432 | 433 | // This call populates the struct in rwc struct 434 | rmap_walk_func(derived_page, rwc_p); 435 | 436 | // Fill cacheline struct with values obtained from rmap_walk_func 437 | (buf->cachelines[way]).pid = process_data_struct.pid; 438 | if(process_data_struct.addr != 0) { 439 | #if FULL_ADDRESS == 0 440 | (buf->cachelines[way]).addr = process_data_struct.addr; 441 | #else 442 | (buf->cachelines[way]).addr = process_data_struct.addr | (((u64)physical_address << 1) & 0xfff); 443 | #endif 444 | } 445 | } 446 | 447 | return 0; 448 | } 449 | 450 | static int __dump_index_noresolve(int index, struct cache_set* buf) 451 | { 452 | int way; 453 | u32 physical_address; 454 | 455 | for (way = 0; way < WAYS; way++) { 456 | get_tag(index, way, &physical_address); 457 | if (!physical_address) 458 | continue; 459 | 460 | // Initalize struct 461 | (buf->cachelines[way]).pid = 0; //process_data_struct->pid;// = 0; 462 | (buf->cachelines[way]).addr = ((u64)physical_address); //process_data_struct->addr;// = 0; 463 | 464 | } 465 | 466 | return 0; 467 | } 468 | 469 | /* Invoke a smaller-footprint function in case address resolution has 470 | * not been requested */ 471 | static int dump_index(int index, struct cache_set* buf) 472 | { 473 | if (flags & DUMPCACHE_CMD_RESOLVE_EN_SHIFT) 474 | return __dump_index_resolve(index, buf); 475 | else 476 | return __dump_index_noresolve(index, buf); 477 | } 478 | 479 | static int dump_all_indices(void) { 480 | int i = 0; 481 | for (i = 0; i < CACHESETS_TO_WRITE; i++) { 482 | if (dump_index(i, &cur_sample->sets[i]) == 1){ 483 | //printk(KERN_INFO "Error dumping index: %d", i); 484 | return 1; 485 | } 486 | } 487 | return 0; 488 | } 489 | 490 | /* ProcFS interface definition */ 491 | static int dumpcache_open(struct inode *inode, struct file *filp) 492 | { 493 | int ret; 494 | 495 | if (!cur_sample) { 496 | pr_err("Something went horribly wrong. Invalid buffer.\n"); 497 | return -EBADFD; 498 | } 499 | 500 | ret = seq_open(filp, &dumpcache_seq_ops); 501 | return ret; 502 | } 503 | 504 | int init_module(void) 505 | { 506 | //printk(KERN_INFO "dumpcache module is loaded\n"); 507 | dump_all_indices_done = 0; 508 | 509 | pr_info("Initializing SHUTTER. Entries: Aperture1 = %ld, Aperture2 = %ld\n", 510 | CACHE_BUF_COUNT1, CACHE_BUF_COUNT2); 511 | 512 | /* Resolve the rmap_walk_func required to resolve physical 513 | * address to virtual addresses */ 514 | if (!rmap_walk_func) { 515 | /* Attempt to find symbol */ 516 | preempt_disable(); 517 | mutex_lock(&module_mutex); 518 | rmap_walk_func = (void*) kallsyms_lookup_name("rmap_walk_locked"); 519 | mutex_unlock(&module_mutex); 520 | preempt_enable(); 521 | 522 | /* Have we found a valid symbol? */ 523 | if (!rmap_walk_func) { 524 | pr_err("Unable to find rmap_walk symbol. Aborting.\n"); 525 | return -ENOSYS; 526 | } 527 | } 528 | 529 | /* Map buffer apertures to be accessible from kernel mode */ 530 | __buf_start1 = (struct cache_sample *) ioremap_nocache(CACHE_BUF_BASE1, CACHE_BUF_SIZE1); 531 | __buf_start2 = (struct cache_sample *) ioremap_nocache(CACHE_BUF_BASE2, CACHE_BUF_SIZE2); 532 | 533 | /* Check that we are all good! */ 534 | if(/*!__buf_start1 ||*/ !__buf_start2) { 535 | pr_err("Unable to io-remap buffer space.\n"); 536 | return -ENOMEM; 537 | } 538 | 539 | /* Set default flags, counter, and current sample buffer */ 540 | flags = 0; 541 | cur_buf = 0; 542 | cur_sample = sample_from_index(0); 543 | 544 | /* Setup proc interface */ 545 | proc_create(MODNAME, 0644, NULL, &dumpcache_fops); 546 | return 0; 547 | } 548 | 549 | void cleanup_module(void) 550 | { 551 | //printk(KERN_INFO "dumpcache module is unloaded\n"); 552 | if(__buf_start1) { 553 | iounmap(__buf_start1); 554 | __buf_start1 = NULL; 555 | } 556 | 557 | if(__buf_start2) { 558 | iounmap(__buf_start2); 559 | __buf_start2 = NULL; 560 | } 561 | 562 | remove_proc_entry(MODNAME, NULL); 563 | } 564 | 565 | #pragma GCC pop_options 566 | MODULE_LICENSE("GPL"); 567 | -------------------------------------------------------------------------------- /experiments/results/plot_scripts/plot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | ####################################################### 4 | # # 5 | # Cache snapshot plotting functions # 6 | # # 7 | # Author: Renato Mancuso (BU) # 8 | # Date: April 2020 # 9 | # # 10 | ####################################################### 11 | 12 | import math 13 | import os 14 | import os.path 15 | import sys 16 | import numpy as np 17 | import commands 18 | import operator 19 | import shutil 20 | import matplotlib.pyplot as plt 21 | import matplotlib.ticker as ticker 22 | from matplotlib.ticker import MaxNLocator 23 | import matplotlib.gridspec as gridspec 24 | from collections import OrderedDict 25 | import matplotlib.colors as mcolors 26 | 27 | from proc_maps_parse import * 28 | 29 | def autoselect_regions(spect, pid, count): 30 | 31 | ## Take regions from second dump 32 | regions = spect.dumps[5].pid_regions[pid] 33 | 34 | totals = [] 35 | for i, r in enumerate(regions): 36 | totals.append((i, spect.get_total_blocks_in_region(pid, i), \ 37 | spect.get_range_blocks_in_region(pid, i))) 38 | 39 | totals = sorted(totals, key=operator.itemgetter(1), reverse=False) 40 | totals = totals[-count:] 41 | 42 | ranges = [max(1, math.log(1 + x[2] / 0x1000)) for x in totals] 43 | totals = [x[0] for x in totals] 44 | 45 | # Compute appropriate horizontal ratios 46 | print ranges 47 | 48 | # return total and horizontal ratios 49 | return (totals, ranges) 50 | 51 | def plot_spectrum(spect, pid, path, scale = 1.5): 52 | 53 | ann = [] 54 | axs = [] 55 | 56 | fig = plt.figure(figsize=(10, 5)) 57 | 58 | # Select regions to plot, expressed as IDs 59 | sel_regions, h_r = autoselect_regions(spect, pid, 4) 60 | 61 | print "Regions: " + str(sel_regions) 62 | # Select ratios for regions 63 | #h_r = [1, 1, 2, 5] 64 | 65 | cols = ["r", "green", "black", "blue", "yellow"] 66 | 67 | #fig, axs = plt.subplots(2, sharex=True, sharey=False, figsize=(10, 5)) 68 | # ann.append(plot_spectrum_region(spect, pid, 3, axs[1], 'r')) 69 | # ann.append(plot_spectrum_region(spect, pid, 0, axs[0], 'b')) 70 | 71 | spec = gridspec.GridSpec(ncols=1, nrows=len(sel_regions), height_ratios=h_r, hspace=0.15) 72 | 73 | cmap=plt.get_cmap("Dark2") 74 | 75 | for i, r in enumerate(sel_regions): 76 | axs.append(fig.add_subplot(spec[i])) 77 | ann.append(plot_spectrum_region_heatm(spect, pid, r, axs[-1], cols[i], h_r[i], scale)) 78 | 79 | # Hide x labels and tick labels for all but bottom plot. 80 | for ax in axs: 81 | ax.label_outer() 82 | 83 | plt.show() 84 | fig.savefig(path + "/plot.png", dpi=fig.dpi, bbox_extra_artists=(ann), bbox_inches='tight'); 85 | fig.savefig(path + "/plot.pdf", dpi=fig.dpi, bbox_extra_artists=(ann), bbox_inches='tight'); 86 | 87 | def plot_spectrum_region(spect, pid, region, axs, col = "r", reg_scale = 1, point_scale = 1.5): 88 | 89 | min_y = None 90 | max_y = None 91 | reg = None 92 | 93 | #print "==== REG %i === " % (region) 94 | 95 | for i, d in enumerate(spect.dumps): 96 | acc = d.pid_accesses[pid] 97 | 98 | if region not in acc.regions_to_pages: 99 | continue 100 | 101 | pages = np.array(acc.regions_to_pages[region]) 102 | reg = d.pid_regions[pid] 103 | area = acc.get_weights(pages, point_scale) 104 | reg_size = reg[region].end - reg[region].start 105 | 106 | pages = (pages - reg[region].start) / 0x1000 107 | 108 | #print "%i) PAGES: %i" % (i, (cur_reg[region].end - cur_reg[region].start) / 0x1000) 109 | 110 | x = [i]*len(acc.regions_to_pages[region]) 111 | axs.scatter(x, pages, s=area, color=col, marker="s", alpha=0.5) 112 | #axs.hist2d(x, pages, weights=area, normed=False) 113 | 114 | if len(pages) > 0: 115 | cur_min = min(pages) 116 | cur_max = max(pages) 117 | 118 | if min_y == None or min_y > cur_min: 119 | min_y = cur_min 120 | 121 | if max_y == None or max_y < cur_max: 122 | max_y = cur_max 123 | 124 | print hex(min_y) 125 | print hex(max_y) 126 | 127 | axs.set_xlim((-0.5, len(spect.dumps)-0.5)) 128 | 129 | axs.set_xlabel("Snapshot #") 130 | 131 | print "%i) MAX %i - MIN %i" % (region, max_y, min_y) 132 | 133 | # Select multiple locato for axis depending on size of axis 134 | if (max_y - min_y > 3 and reg_scale < 2): 135 | axs.yaxis.set_major_locator(ticker.MultipleLocator((max_y - min_y) / 2)) 136 | axs.set_ylim((min_y - 0.5, max_y + 0.5)) 137 | elif (max_y - min_y > 3): 138 | axs.yaxis.set_major_locator(ticker.MultipleLocator((max_y - min_y) / 6)) 139 | #axs.set_ylim((min_y - 0.5, max_y + 0.5)) 140 | elif (max_y != min_y): 141 | axs.yaxis.set_major_locator(ticker.MultipleLocator(1)) 142 | else: 143 | axs.yaxis.set_major_locator(ticker.FixedLocator([min_y])) 144 | 145 | axs.xaxis.set_major_locator(ticker.MultipleLocator(max(1, len(spect.dumps)/20))) 146 | axs.get_yaxis().set_major_formatter(ticker.FuncFormatter(lambda x, pos: "+%i" % (x))) 147 | 148 | ## Add annotation to y axis to show name of the area and arrow range 149 | off = -(len(spect.dumps))/float(8.5) 150 | 151 | if (min_y == max_y): 152 | axs.annotate('', xy=(off, min_y-0.05), xytext=(off, max_y+0.05), 153 | arrowprops=dict(arrowstyle='|-|',facecolor='red'), 154 | annotation_clip=False) 155 | 156 | else: 157 | axs.annotate('', xy=(off, min_y), xytext=(off, max_y), 158 | arrowprops=dict(arrowstyle='|-|',facecolor='red'), 159 | annotation_clip=False) 160 | 161 | center = (max_y + min_y) / float(2) 162 | ann = axs.annotate(reg[region].short_name, xy=(1.8*off, center), xytext=(1.8*off, center), 163 | annotation_clip=False) 164 | 165 | return ann 166 | 167 | 168 | def plot_spectrum_region_heatm(spect, pid, region, axs, col = "r", reg_scale = 1, point_scale = 1.5): 169 | 170 | min_y = None 171 | max_y = None 172 | reg = None 173 | 174 | #print "==== REG %i === " % (region) 175 | 176 | x = [] 177 | y = [] 178 | 179 | for i, d in enumerate(spect.dumps): 180 | acc = d.pid_accesses[pid] 181 | 182 | if region not in acc.regions_to_pages: 183 | continue 184 | 185 | pages = np.array(acc.regions_to_pages[region]) 186 | reg = d.pid_regions[pid] 187 | reg_size = reg[region].end - reg[region].start 188 | offsets = (pages - reg[region].start) / 0x1000 189 | 190 | for p in range(0, len(pages)): 191 | blocks = acc.pages[pages[p]] 192 | x.extend([i] * blocks) 193 | y.extend([offsets[p]] * blocks) 194 | 195 | if len(offsets) > 0: 196 | cur_min = min(offsets) 197 | cur_max = max(offsets) 198 | 199 | if min_y == None or min_y > cur_min: 200 | min_y = cur_min 201 | 202 | if max_y == None or max_y < cur_max: 203 | max_y = cur_max 204 | 205 | print hex(min_y) 206 | print hex(max_y) 207 | 208 | axs.hist2d(x, y, normed=True, bins=[len(spect.dumps), min(max_y - min_y + 1, 50)], \ 209 | norm=mcolors.PowerNorm(0.3), range=[(-0.5, len(spect.dumps)-0.5), (min_y, max_y)]) 210 | 211 | axs.set_xlim((-0.5, len(spect.dumps)-0.5)) 212 | 213 | axs.set_xlabel("Snapshot #") 214 | 215 | print "%i) MAX %i - MIN %i" % (region, max_y, min_y) 216 | 217 | # Select multiple locato for axis depending on size of axis 218 | if (max_y - min_y > 3 and reg_scale < 2): 219 | axs.yaxis.set_major_locator(ticker.MultipleLocator((max_y - min_y) / 2)) 220 | axs.set_ylim((min_y - 0.5, max_y + 0.5)) 221 | elif (max_y - min_y > 3): 222 | axs.yaxis.set_major_locator(ticker.MultipleLocator((max_y - min_y) / 6)) 223 | #axs.set_ylim((min_y - 0.5, max_y + 0.5)) 224 | elif (max_y != min_y): 225 | axs.yaxis.set_major_locator(ticker.MultipleLocator(1)) 226 | else: 227 | axs.yaxis.set_major_locator(ticker.FixedLocator([min_y])) 228 | 229 | axs.xaxis.set_major_locator(ticker.MultipleLocator(max(1, len(spect.dumps)/20))) 230 | axs.get_yaxis().set_major_formatter(ticker.FuncFormatter(lambda x, pos: "+%i" % (x))) 231 | 232 | ## Add annotation to y axis to show name of the area and arrow range 233 | off = -(len(spect.dumps))/float(8.5) 234 | 235 | if (min_y == max_y): 236 | axs.annotate('', xy=(off, min_y-0.05), xytext=(off, max_y+0.05), 237 | arrowprops=dict(arrowstyle='|-|',facecolor='red'), 238 | annotation_clip=False) 239 | 240 | else: 241 | axs.annotate('', xy=(off, min_y), xytext=(off, max_y), 242 | arrowprops=dict(arrowstyle='|-|',facecolor='red'), 243 | annotation_clip=False) 244 | 245 | center = (max_y + min_y) / float(2) 246 | ann = axs.annotate(reg[region].short_name, xy=(1.8*off, center), xytext=(1.8*off, center), 247 | annotation_clip=False) 248 | 249 | return ann 250 | 251 | def plot_multipid_seq(spects, path, captions = [], bw = 0, ms = []): 252 | 253 | ann = [] 254 | axs = [] 255 | 256 | fig = plt.figure(figsize=(10, 5)) 257 | count = len(spects) 258 | 259 | # Select regions to plot, expressed as IDs 260 | spec = gridspec.GridSpec(ncols=1, nrows=count, hspace=0.15) 261 | 262 | cmap=plt.get_cmap("Dark2") 263 | 264 | for i, s in enumerate(spects): 265 | if i <= 0: 266 | axs.append(fig.add_subplot(spec[i])) 267 | else: 268 | axs.append(fig.add_subplot(spec[i], sharey=axs[0])) 269 | 270 | if ms: 271 | __plot_multipid_single(s, s.other_pids, axs[-1], count, captions, 272 | bw, "period = %i ms" % (ms[i])) 273 | else: 274 | __plot_multipid_single(s, s.other_pids, axs[-1], count, captions, bw, "") 275 | 276 | # Hide x labels and tick labels for all but bottom plot. 277 | for ax in axs: 278 | ax.label_outer() 279 | 280 | pids = len(spects[-1].other_pids) + 2 281 | ann.append(axs[0].legend(bbox_to_anchor=(0., 1.02, 1., .102), 282 | loc='lower left', ncol=pids, mode="expand", 283 | borderaxespad=0.)) 284 | 285 | axs[0].set_ylim((0, 2*1024*1024/64)) 286 | plt.xlabel("Snapshot #") 287 | plt.ylabel("L2 Cache Occupancy") 288 | 289 | plt.show() 290 | fig.savefig(path + "/plot_wss.png", dpi=fig.dpi, bbox_extra_artists=(ann), bbox_inches='tight'); 291 | fig.savefig(path + "/plot_wss.pdf", dpi=fig.dpi, bbox_extra_artists=(ann), bbox_inches='tight'); 292 | 293 | 294 | def __plot_multipid_single(spect, pids, axs, plot_count, captions = [], bw = 0, annot = ""): 295 | 296 | ids = [] 297 | bars = {} 298 | 299 | pids.append("other") 300 | pids.append("unres") 301 | 302 | if not captions: 303 | captions = array(pids) 304 | 305 | idx = 0 306 | for p in pids: 307 | bars[p] = [] 308 | 309 | if idx >= len(captions): 310 | captions.append(p) 311 | 312 | idx += 1 313 | 314 | print captions 315 | 316 | cmap=plt.get_cmap("viridis", len(pids)) 317 | 318 | colors = cmap.colors 319 | 320 | if bw == 0: 321 | w = 0.5 322 | else: 323 | w = bw 324 | 325 | start_p = {} 326 | 327 | for i, d in enumerate(spect.dumps): 328 | start = 0 329 | 330 | ids.append(i) 331 | idx = 0 332 | for p in pids: 333 | 334 | if p == "other": 335 | count = d.not_in_pidfile 336 | elif p == "unres": 337 | count = d.unresolved 338 | else: 339 | count = d.pid_accesses[p].get_count() 340 | 341 | bars[p].append(count) 342 | 343 | if i > 0 and bw != 0: 344 | axs.fill([i-1 + w/2, i-1 + w/2, i - w/2, i - w/2], \ 345 | [start_p[p], start_p[p] + bars[p][-2], start + bars[p][-1], start], \ 346 | color=colors[idx], alpha = 0.5, lw=0) 347 | 348 | start_p[p] = start 349 | start += count 350 | idx += 1 351 | 352 | stack = [0]*len(spect.dumps) 353 | 354 | idx = 0 355 | for p in pids: 356 | axs.bar(ids, bars[p], bottom=stack, width=w, color=colors[idx], label=captions[idx]) 357 | idx += 1 358 | stack = np.add(stack, bars[p]).tolist() 359 | 360 | axs.get_yaxis().set_major_formatter(\ 361 | ticker.FuncFormatter(lambda x, pos: "%0.0f%%" % ((100*x)/(2*1024*1024/64)))) 362 | 363 | axs.get_yaxis().set_major_locator(ticker.MultipleLocator(3276.8 * plot_count)) 364 | axs.get_xaxis().set_major_locator(ticker.MultipleLocator(max(1,len(spect.dumps)/20))) 365 | 366 | axs.set_xlim((0-float(w)/2, len(spect.dumps) -1 + float(w)/2)) 367 | 368 | bbox_props = dict(boxstyle="round,pad=0.5", fc="w", ec="k", lw=2, alpha=0.8) 369 | 370 | if(annot != ""): 371 | axs.annotate(annot, 372 | xy=(0, 0.5), 373 | xycoords='axes fraction', 374 | xytext=(0.85, 0.15), 375 | bbox=bbox_props, 376 | arrowprops=None) 377 | 378 | def plot_sc_synth(): 379 | ## 5 ms interval (default) 380 | # path = "../single_synth/" 381 | # spect = MemSpectrum(14245, path + "/pids.txt", 1, 36) 382 | # labels = ["parent", "synth"] 383 | # #plot_multipid_seq([spect], path, labels, 0.5) 384 | # plot_spectrum(spect, 14245, path) 385 | 386 | ## 10 ms interval 387 | path = "../synth_new_10/" 388 | spect10 = MemSpectrum(15295, path + "/pids.txt", 1, 18) 389 | 390 | ## 20 ms interval 391 | path = "../synth_new_20/" 392 | spect20 = MemSpectrum(15304, path + "/pids.txt", 1, 18) 393 | 394 | # Combined plot 395 | plot_multipid_seq([spect, spect10, spect20], path, labels, 0.5) 396 | 397 | def plot_sc_synth_step(): 398 | ## 1 ms interval 399 | # path = "../synth_step_1/" 400 | # spect1 = MemSpectrum(16738, path + "/pids.txt", 1, 141) 401 | # plot_spectrum(spect1, 16738, path) 402 | 403 | ## 5 ms interval (default) 404 | path = "../single_synth_step/" 405 | spect = MemSpectrum(13974, path + "/pids.txt", 1, 28) 406 | labels = ["parent", "synth"] 407 | plot_spectrum(spect, 13974, path) 408 | 409 | ## 10 ms interval 410 | path = "../synth_step_10/" 411 | spect10 = MemSpectrum(16600, path + "/pids.txt", 1, 18) 412 | 413 | ## 20 ms interval 414 | path = "../synth_step_20/" 415 | spect20 = MemSpectrum(16611, path + "/pids.txt", 1, 18) 416 | 417 | ## 30 ms interval 418 | path = "../synth_step_30/" 419 | spect30 = MemSpectrum(17222, path + "/pids.txt", 1, 18) 420 | 421 | ## 40 ms interval 422 | path = "../synth_step_40/" 423 | spect40 = MemSpectrum(17194, path + "/pids.txt", 1, 18) 424 | 425 | ## 50 ms interval 426 | path = "../synth_step_50/" 427 | spect50 = MemSpectrum(17104, path + "/pids.txt", 0, 18) 428 | 429 | # Combined plot 430 | plot_multipid_seq([spect20, spect30, spect40], path, labels, 0.5) 431 | 432 | def plot_sc_synth_15step(): 433 | labels = ["parent", "synth"] 434 | 435 | ## 2 ms interval 436 | path = "../synth_15step_2/" 437 | spect2 = MemSpectrum(19840, path + "/pids.txt", 1, 100) 438 | 439 | path = "../synth_15step_5/" 440 | spect5 = MemSpectrum(18955, path + "/pids.txt", 1, 100) 441 | 442 | path = "../synth_15step_10/" 443 | spect10 = MemSpectrum(18971, path + "/pids.txt", 1, 100) 444 | plot_multipid_seq([spect2, spect5, spect10], path, labels, 0.5, [2, 5, 10]) 445 | 446 | return 447 | ## 5 ms interval 448 | # path = "../synth_15step_5/" 449 | # spect5 = MemSpectrum(17818, path + "/pids.txt", 1, 100) 450 | 451 | # plot_spectrum(spect5, 17818, path) 452 | 453 | ## 10 ms interval 454 | path = "../synth_15step_10/" 455 | spect10 = MemSpectrum(17827, path + "/pids.txt", 1, 10) 456 | 457 | ## 20 ms interval 458 | path = "../synth_15step_20/" 459 | spect20 = MemSpectrum(17830, path + "/pids.txt", 1, 10) 460 | 461 | ## 30 ms interval 462 | path = "../synth_15step_30/" 463 | spect30 = MemSpectrum(17833, path + "/pids.txt", 1, 10) 464 | 465 | ## 40 ms interval 466 | path = "../synth_15step_40/" 467 | spect40 = MemSpectrum(17836, path + "/pids.txt", 1, 10) 468 | 469 | # Combined plot 470 | plot_multipid_seq([spect10, spect20, spect30], path, labels, 0.5) 471 | 472 | 473 | def plot_sc_sd_vbs(): 474 | path = "../sc_4sdvbs_vga/" 475 | spect = MemSpectrum(0, path + "/pids.txt", 1, 700) 476 | labels = ["parent", "track. (vga)", "mser (vga)", "sift (cif)", "disp. (cif)"] 477 | plot_multipid_seq([spect], path, labels, 1, False) 478 | 479 | def plot_sc_sd_vbs_nrt(): 480 | path = "../sc_4sdvbs_vga_nrt/" 481 | spect = MemSpectrum(0, path + "/pids.txt", 1, 700) 482 | labels = ["parent", "track. (vga)", "mser (vga)", "sift (cif)", "disp. (cif)"] 483 | plot_multipid_seq([spect], path, labels, 1, False) 484 | 485 | def plot_mc_sd_vbs(): 486 | path = "../mc_4sdvbs_vga/" 487 | spect = MemSpectrum(0, path + "/pids.txt", 1, 700) 488 | labels = ["parent", "track. (vga)", "mser (vga)", "sift (cif)", "disp. (cif)"] 489 | plot_multipid_seq([spect], path, labels, 1, False) 490 | 491 | def plot_mc_4c_sd_vbs(): 492 | path = "../mc_4c_4sdvbs_vga/" 493 | spect = MemSpectrum(0, path + "/pids.txt", 1, 700) 494 | labels = ["parent", "track. (vga)", "mser (vga)", "sift (cif)", "disp. (cif)"] 495 | plot_multipid_seq([spect], path, labels, 1, False) 496 | 497 | def plot_solo(bm, labels): 498 | path = "../sdvbs_solo/" + bm + "/" 499 | spect = MemSpectrum(0, path + "/pids.txt", 1, 300) 500 | plot_multipid_seq([spect], path, labels, 1, False) 501 | 502 | def plot_sc_sd_vbs_rtprio(): 503 | path = "../single_conc_sd_vbs_rtprio/" 504 | spect = MemSpectrum(0, path + "/pids.txt", 1, 225) 505 | labels = ["parent", "disp. (vga)", "mser (vga)", "sift (cif)", "track. (cif)"] 506 | plot_multipid(spect, spect.other_pids, path, labels, 1) 507 | 508 | def plot_mc_4_sd_vbs_rtprio(): 509 | path = "../mult_4_conc_sd_vbs_rtprio/" 510 | spect = MemSpectrum(0, path + "/pids.txt", 1, 225) 511 | labels = ["parent", "disp. (vga)", "mser (vga)", "sift (cif)", "track. (cif)"] 512 | plot_multipid(spect, spect.other_pids, path, labels, 1) 513 | 514 | def plot_mc_2_sd_vbs_rtprio(): 515 | path = "../mult_2_conc_sd_vbs_rtprio/" 516 | spect = MemSpectrum(0, path + "/pids.txt", 1, 201) 517 | labels = ["parent", "disp. (vga)", "mser (vga)"] 518 | plot_multipid(spect, spect.other_pids, path, labels, 1) 519 | 520 | def plot_sc_disp_cif(): 521 | path = "../disparity_cif/" 522 | pid = 20843 523 | spect = MemSpectrum(pid, path + "/pids.txt", 1, 69) 524 | plot_spectrum(spect, pid, path, 0.3) 525 | 526 | def plot_simple(): 527 | path = "../syn_dual/" 528 | pid = 15380 529 | spect = MemSpectrum(pid, path + "/pids.txt", 1, 30) 530 | plot_spectrum(spect, pid, path, 0.3) 531 | 532 | def plot_simple_tr(): 533 | path = "../syn_dual_tr/" 534 | pid = 15158 535 | spect = MemSpectrum(pid, path + "/pids.txt", 1, 30) 536 | plot_spectrum(spect, pid, path, 0.3) 537 | 538 | def plot_sc_disp_vga(): 539 | path = "../disparity_vga/" 540 | pid = 20812 541 | spect = MemSpectrum(pid, path + "/pids.txt", 1, 200) 542 | plot_spectrum(spect, pid, path, 0.3) 543 | 544 | def plot_sc_mser_vga(): 545 | path = "../mser_vga/" 546 | pid = 20868 547 | spect = MemSpectrum(pid, path + "/pids.txt", 1, 200) 548 | plot_spectrum(spect, pid, path, 0.3) 549 | 550 | def plot_sc_sift_vga(): 551 | path = "../sift_vga/" 552 | pid = 20891 553 | spect = MemSpectrum(pid, path + "/pids.txt", 1, 200) 554 | plot_spectrum(spect, pid, path, 0.3) 555 | 556 | def cp_plots_to_paper(): 557 | dirs = ["../single_synth/", "../synth_new_20/", "../single_synth_step/", "../single_conc_sd_vbs/", "../single_conc_sd_vbs_rtprio/", "../mult_4_conc_sd_vbs_rtprio/", "../mult_2_conc_sd_vbs_rtprio/", "../disparity_cif/", "../disparity_vga/"] 558 | 559 | dest = "../../paper_ECRTS20/figs/" 560 | 561 | for d in dirs: 562 | prefix = d.split("/")[-2] + "_" 563 | 564 | name = "plot.pdf" 565 | if os.path.isfile(d + name): 566 | shutil.copyfile(d + name, dest + prefix + name) 567 | 568 | name = "plot_pids.pdf" 569 | if os.path.isfile(d + name): 570 | shutil.copyfile(d + name, dest + prefix + name) 571 | 572 | name = "plot_wss.pdf" 573 | if os.path.isfile(d + name): 574 | shutil.copyfile(d + name, dest + prefix + name) 575 | 576 | if __name__ == '__main__': 577 | 578 | if len(sys.argv) > 2: 579 | 580 | if (False): 581 | 582 | # CMD: python plot.py 8734 ../single_executable_synth/ 583 | 584 | pid = int(sys.argv[1]) 585 | path = sys.argv[2] 586 | 587 | spect = MemSpectrum(pid, path + "/pids.txt", 1, 14) 588 | #spect.get_stats_per_region() 589 | plot_spectrum(spect, pid, path) 590 | 591 | elif (False): 592 | 593 | # CMD: python plot.py 7105 ../sd_vbs_concurr/ 594 | 595 | pid = int(sys.argv[1]) 596 | path = sys.argv[2] 597 | 598 | spect = MemSpectrum(pid, path + "/pids.txt", 5, 300) 599 | 600 | #spect.get_stats_per_region() 601 | plot_spectrum(spect, pid, path) 602 | 603 | elif (False): 604 | 605 | # CMD: python plot.py 7105 ../sd_vbs_concurr/ 606 | 607 | pids = [] 608 | for arg in sys.argv[1:-1]: 609 | pids.append(int(arg)) 610 | 611 | path = sys.argv[-1] 612 | spect = MemSpectrum(pids[0], path + "/pids.txt", 5, 100) 613 | 614 | plot_multipid(spect, spect.other_pids, path) 615 | 616 | 617 | elif (False): 618 | 619 | # Everything passed via params 620 | pids = [] 621 | for arg in sys.argv[1:-1]: 622 | pids.append(int(arg)) 623 | 624 | path = sys.argv[-1] 625 | first = int(sys.argv[-3]) 626 | last = int(sys.argv[-2]) 627 | 628 | spect = MemSpectrum(pids[0], path + "/pids.txt", first, last) 629 | 630 | plot_multipid(spect, spect.other_pids, path, ["parent", "obs"]) 631 | 632 | else: 633 | 634 | # Single core, synthetic experiments: 635 | #plot_sc_synth() 636 | #plot_sc_synth_step() 637 | #plot_sc_synth_15step() 638 | 639 | #plot_sc_disp_vga() 640 | #plot_sc_disp_cif() 641 | #plot_sc_mser_vga() 642 | #plot_sc_sift_vga() 643 | 644 | #plot_sc_sd_vbs() 645 | #plot_sc_sd_vbs_nrt() 646 | #plot_mc_sd_vbs() 647 | #plot_mc_4c_sd_vbs() 648 | plot_simple() 649 | plot_simple_tr() 650 | 651 | #plot_sc_sd_vbs_rtprio() 652 | #plot_mc_4_sd_vbs_rtprio() 653 | #plot_mc_2_sd_vbs_rtprio() 654 | 655 | #plot_solo("disparity", ["par", "disparity (vga)"]) 656 | #plot_solo("sift", ["par", "sift (vga)"]) 657 | #plot_solo("mser", ["par", "mser (vga)"]) 658 | #plot_solo("tracking", ["par", "tracking (vga)"]) 659 | 660 | #cp_plots_to_paper() 661 | 662 | else: 663 | print(sys.argv[0] + " /proc//maps") 664 | -------------------------------------------------------------------------------- /experiments/snapshot.c: -------------------------------------------------------------------------------- 1 | /*************************************************************/ 2 | /* */ 3 | /* Unified code to launch benchmarks with periodic */ 4 | /* snapshotting. This code was initially written by: */ 5 | /* Steven Brzozowski (BU) */ 6 | /* Major rev. by: Dharmesh Tarapore (BU) */ 7 | /* Major rev. by: Renato Mancuso (BU) */ 8 | /* */ 9 | /* Date: April 2020 */ 10 | /* */ 11 | /*************************************************************/ 12 | 13 | #define _GNU_SOURCE 14 | #include "params.h" 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #define MAX_BENCHMARKS 20 24 | #define PARENT_CPU 2 25 | #define SNAP_PERIOD_MS 5 26 | 27 | #define USAGE_STR "Usage: %s [-rmafi] [-o outpath] [-p period_ms] " \ 28 | "\"benchmark 1\", ..., \"benchmark n\"\n" \ 29 | "Options:\n" \ 30 | "-r\tSet real-time priorities. Parent has highest priority,\n" \ 31 | " \tthe priority of the benchmarks is set in decreasing order.\n" \ 32 | "\n" \ 33 | "-m\tMimic only. No cache snapshotting but do everything else.\n" \ 34 | "\n" \ 35 | "-a\tAsynchrnonous mode. Do not send SIGTOP/SIGCONT to benchmarks.\n" \ 36 | "\n" \ 37 | "-f\tForce output. Overwrite content of output directory.\n" \ 38 | "\n" \ 39 | "-i\tIsolation mode. Pin parent alone on CPU "STR(PARENT_CPU)".\n" \ 40 | "\n" \ 41 | "-o\tOutput files to custom directory instead of " SCRATCHSPACE_DIR ".\n" \ 42 | "\n" \ 43 | "-p\tSet custom period between samples expressed in msec. Default is " STR(SNAP_PERIOD_MS) " msec.\n\n" \ 44 | "-n\tDo not perform physical->virtual address translation in the kernel.\n\n" \ 45 | "-t\tOperate in transparent mode, i.e. defer acquisition of samples to disk to the end.\n\n" \ 46 | "-l\tDo not acquire the memory layout of the observed benchmarks.\n\n" \ 47 | "-h\tOperate in overhead measurement mode. Only 2 back-to-back snapshots will be acquired.\n" \ 48 | "\n" 49 | 50 | #define MS_TO_NS(ms) \ 51 | (ms * 1000 * 1000) 52 | #define MALLOC_CMD_PAD (32) 53 | 54 | 55 | int flag_rt = 0; 56 | int flag_out = 0; 57 | int flag_force = 0; 58 | int flag_isol = 0; 59 | int flag_async = 0; 60 | int flag_mimic = 0; 61 | int flag_resolve = 1; 62 | int flag_transparent = 0; 63 | int flag_bm_layout = 1; 64 | int flag_overhead = 0; 65 | int flag_periodic = 1; 66 | 67 | /* Default sampling period is 5 ms */ 68 | long int snap_period_ms = SNAP_PERIOD_MS; 69 | 70 | char * outdir = SCRATCHSPACE_DIR; 71 | 72 | int bm_count = 0; 73 | int running_bms; 74 | char * bms [MAX_BENCHMARKS]; 75 | pid_t pids [MAX_BENCHMARKS]; 76 | 77 | int max_prio; 78 | 79 | volatile int done = 0; 80 | int snapshots = 0; 81 | 82 | /* Use user-specified parameters to configure the kernel module for 83 | * acquisition */ 84 | int config_shutter(void); 85 | 86 | /* Function to spawn all the listed benchmarks */ 87 | void launch_benchmarks(void); 88 | 89 | /* Handler for SIGCHLD signal to detect benchmark termination */ 90 | void proc_exit_handler (int signo, siginfo_t * info, void * extra); 91 | 92 | /* Ask the kernel to acquire a new snapshot */ 93 | void acquire_new_snapshot(void); 94 | 95 | /* Handler for SIGRTMAX signal to initiate new snapshot */ 96 | void snapshot_handler (int signo, siginfo_t * info, void * extra); 97 | 98 | /* Set real-time SCHED_FIFO scheduler with given priority */ 99 | void set_realtime(int prio); 100 | 101 | /* Only change RT prio of calling process */ 102 | void change_rt_prio(int prio); 103 | 104 | /* Set non-real-time SCHED_OTHER scheduler */ 105 | void set_non_realtime(void); 106 | 107 | /* Install periodic snapshot handler and wait for completion using signals */ 108 | void wait_completion(void); 109 | 110 | /* Entry function to interface with the kernel module via the proc interface */ 111 | void read_cache_to_file(char * filename, int index); 112 | 113 | /* Function to complete execution */ 114 | void wrap_up(void); 115 | 116 | int main (int argc, char ** argv) 117 | { 118 | /* Parse command line */ 119 | int opt, res; 120 | struct stat dir_stat; 121 | 122 | while ((opt = getopt(argc, argv, "-rmafio:p:ntlh")) != -1) { 123 | switch (opt) { 124 | case 1: 125 | { 126 | /* Benchmark to run parameter */ 127 | bms[bm_count++] = argv[optind - 1]; 128 | break; 129 | } 130 | case 'r': 131 | { 132 | /* RT scheduler requested */ 133 | flag_rt = 1; 134 | break; 135 | } 136 | case 'a': 137 | { 138 | /* Asynchronous requested */ 139 | flag_async = 1; 140 | break; 141 | } 142 | case 'm': 143 | { 144 | /* Mimic only --- no cache dumps performed */ 145 | flag_mimic = 1; 146 | break; 147 | } 148 | case 'i': 149 | { 150 | /* Isolate: make sure child processes are not 151 | * allowed to run on the same CPU as the 152 | * parent */ 153 | flag_isol = 1; 154 | break; 155 | } 156 | case 'f': 157 | { 158 | /* Override content of outdir !!! CAREFUL */ 159 | flag_force = 1; 160 | break; 161 | } 162 | case 'p': 163 | { 164 | /* Set custom sampling period in ms */ 165 | snap_period_ms = strtol(optarg, NULL, 10); 166 | 167 | if (snap_period_ms == 0) 168 | flag_periodic = 0; 169 | 170 | break; 171 | } 172 | case 'n': 173 | { 174 | /* Disable address resolution in the kernel */ 175 | flag_resolve = 0; 176 | break; 177 | } 178 | case 't': 179 | { 180 | /* Request transparent snapshotting */ 181 | flag_transparent = 1; 182 | break; 183 | } 184 | case 'l': 185 | { 186 | /* Do not acquire applications layout files 187 | * with snapshots */ 188 | flag_bm_layout = 0; 189 | break; 190 | } 191 | case 'h': 192 | { 193 | /* Operate in overhead calculation mode. In 194 | * this mode, the scnapshot will be activated 195 | * in one-shot mode after the amount of time 196 | * specified with the -p parameter. Two 197 | * back-to-back snapshots will be collected. */ 198 | flag_overhead = 1; 199 | break; 200 | } 201 | case 'o': 202 | { 203 | /* Custom output dir requested */ 204 | int path_len = strlen(optarg); 205 | outdir = (char *)malloc(path_len+1); 206 | strncpy(outdir, optarg, path_len); 207 | flag_out = 1; 208 | break; 209 | } 210 | default: 211 | { 212 | /* '?' */ 213 | fprintf(stderr, USAGE_STR, argv[0]); 214 | exit(EXIT_FAILURE); 215 | } 216 | } 217 | } 218 | 219 | if (bm_count == 0) { 220 | fprintf(stderr, USAGE_STR, argv[0]); 221 | exit(EXIT_FAILURE); 222 | } 223 | 224 | /* Prepare output directory */ 225 | res = stat(outdir, &dir_stat); 226 | if (!flag_force && res >= 0) { 227 | fprintf(stderr, "Output directory already exists. Use -f to override files.\n"); 228 | exit(EXIT_FAILURE); 229 | } else if (flag_force && res >= 0){ 230 | char * __cmd = (char *)malloc(strlen(outdir) + MALLOC_CMD_PAD); 231 | sprintf(__cmd, "rm -rf %s", outdir); 232 | printf("Erasing content of %s\n", outdir); 233 | /* Do not actually do this for now. Just to be safe. */ 234 | printf("[%s]\n", __cmd); 235 | free(__cmd); 236 | } else { 237 | /* The directory does not exist. */ 238 | mkdir(outdir, 0700); 239 | } 240 | 241 | /* ALWAYS run the parent with top RT priority */ 242 | max_prio = sched_get_priority_max(SCHED_FIFO); 243 | set_realtime(max_prio); 244 | 245 | /* Send setup commands to the kernel module */ 246 | config_shutter(); 247 | 248 | /* Done with command line parsing -- time to fire up the benchmarks */ 249 | launch_benchmarks(); 250 | 251 | /* Done with benchmarks --- wait for completion using an asynch handler */ 252 | wait_completion(); 253 | 254 | /* Almost done - wrap up by creating pid.txt file */ 255 | wrap_up(); 256 | 257 | /* Deallocate any malloc'd memory before exiting */ 258 | if (flag_out) { 259 | free(outdir); 260 | } 261 | 262 | return EXIT_SUCCESS; 263 | } 264 | 265 | static inline int open_mod(void) 266 | { 267 | int fd; 268 | 269 | /* Open dumpcache interface */ 270 | if (((fd = open(PROC_FILENAME, O_RDONLY)) < 0)) { 271 | perror("Failed to open "PROC_FILENAME" file. Is the module inserted?"); 272 | exit(EXIT_FAILURE); 273 | } 274 | 275 | return fd; 276 | } 277 | 278 | /* Use user-specified parameters to configure the kernel module for 279 | * acquisition */ 280 | int config_shutter(void) 281 | { 282 | int dumpcache_fd; 283 | int err; 284 | unsigned long cmd = 0; 285 | 286 | dumpcache_fd = open_mod(); 287 | 288 | /* Whatever is the mode, reset the sample pointer to start 289 | * with. */ 290 | cmd |= DUMPCACHE_CMD_SETBUF_SHIFT; 291 | 292 | /* If we want transparent mode, request buffer auto-increment 293 | * in the kernel */ 294 | if (flag_transparent == 1) { 295 | cmd |= DUMPCACHE_CMD_AUTOINC_EN_SHIFT; 296 | } else { 297 | cmd |= DUMPCACHE_CMD_AUTOINC_DIS_SHIFT; 298 | } 299 | 300 | /* Disable address resolution if requested by the user */ 301 | if (flag_resolve == 1) { 302 | cmd |= DUMPCACHE_CMD_RESOLVE_EN_SHIFT; 303 | } else { 304 | cmd |= DUMPCACHE_CMD_RESOLVE_DIS_SHIFT; 305 | } 306 | 307 | err = ioctl(dumpcache_fd, DUMPCACHE_CMD_CONFIG, cmd); 308 | if (err) { 309 | perror("Shutter configuration command failed"); 310 | exit(EXIT_FAILURE); 311 | } 312 | 313 | printf("Module config OKAY!\n"); 314 | 315 | close(dumpcache_fd); 316 | 317 | return err; 318 | } 319 | 320 | /* Function to spawn all the listed benchmarks */ 321 | void launch_benchmarks (void) 322 | { 323 | int i; 324 | 325 | for (i = 0; i < bm_count; ++i) { 326 | 327 | /* Launch all the BMs one by one */ 328 | pid_t cpid = fork(); 329 | if (cpid == -1) { 330 | perror("fork"); 331 | exit(EXIT_FAILURE); 332 | } 333 | /* Child process */ 334 | if (cpid == 0) { 335 | /* Assume that there is only at most one paramer */ 336 | char * args[3]; 337 | 338 | /* To follow execv's convention*/ 339 | args[2] = NULL; 340 | args[0] = bms[i]; 341 | 342 | if((args[1] = strchr(bms[i], ' '))) { 343 | *args[1] = '\0'; 344 | args[1]++; 345 | } 346 | 347 | /* Set SCHED_FIFO priority if necessary */ 348 | if (flag_rt) { 349 | change_rt_prio(max_prio -1 -i); 350 | } else { 351 | set_non_realtime(); 352 | } 353 | 354 | sched_yield(); 355 | 356 | execv(args[0], args); 357 | 358 | /* This point can only be reached if execl fails. */ 359 | perror("Unable to run benchmark"); 360 | exit(EXIT_FAILURE); 361 | } 362 | /* Parent process */ 363 | else { 364 | /* Keep track of the new bm that has been launched */ 365 | printf("Running: %s (PID = %d, prio = %d)\n", bms[i], cpid, 366 | (flag_rt?(max_prio -1 -i):0)); 367 | 368 | pids[running_bms++] = cpid; 369 | //cpid_arr[i*NUM_SD_VBS_BENCHMARKS_DATASETS+j] = cpid; 370 | } 371 | 372 | 373 | } 374 | 375 | } 376 | 377 | /* Handler for SIGCHLD signal to detect benchmark termination */ 378 | /* Adapted from https://docs.oracle.com/cd/E19455-01/806-4750/signals-7/index.html */ 379 | void proc_exit_handler (int signo, siginfo_t * info, void * extra) 380 | { 381 | int wstat; 382 | pid_t pid; 383 | 384 | (void)signo; 385 | (void)info; 386 | (void)extra; 387 | 388 | for (;;) { 389 | pid = waitpid (-1, &wstat, WNOHANG); 390 | if (pid == 0) 391 | /* No change in the state of the child(ren) */ 392 | return; 393 | else if (pid == -1) { 394 | /* Something went wrong */ 395 | perror("Waitpid() exited with error"); 396 | exit(EXIT_FAILURE); 397 | return; 398 | } 399 | else { 400 | printf ("PID %d Done. Return code: %d\n", pid, WEXITSTATUS(wstat)); 401 | 402 | /* Detect completion of all the benchmarks */ 403 | if(--running_bms == 0) { 404 | done = 1; 405 | return; 406 | } 407 | } 408 | } 409 | } 410 | 411 | /* Copy content of file from src to dst */ 412 | #define BUF_SIZE (1024) 413 | void copy_file(char * src, char * dst) 414 | { 415 | static char buf [BUF_SIZE]; 416 | int src_fd = open(src, O_RDONLY); 417 | ssize_t num_read; 418 | 419 | if (src_fd < 0) 420 | return; 421 | 422 | int dst_fd = open(dst, O_RDWR | O_CREAT | O_TRUNC, 0700); 423 | 424 | if (dst_fd < 0) { 425 | perror("Unable to save maps file."); 426 | exit(EXIT_FAILURE); 427 | } 428 | 429 | while ((num_read = read(src_fd, buf, BUF_SIZE)) > 0) 430 | { 431 | if (write(dst_fd, buf, num_read) != num_read) { 432 | perror("Unable to write maps file."); 433 | exit(EXIT_FAILURE); 434 | } 435 | } 436 | 437 | close(src_fd); 438 | close(dst_fd); 439 | } 440 | 441 | /* Ask the kernel to acquire a new snapshot */ 442 | void acquire_new_snapshot(void) 443 | { 444 | int dumpcache_fd = open_mod(); 445 | int retval; 446 | 447 | retval = ioctl(dumpcache_fd, DUMPCACHE_CMD_SNAPSHOT, 0); 448 | if (retval < 0) { 449 | fprintf(stderr, "Return was %d\n", retval); 450 | perror("Unable to commandeer new snapshot acquisition"); 451 | exit(EXIT_FAILURE); 452 | } 453 | 454 | close(dumpcache_fd); 455 | } 456 | 457 | /* Handler for SIGRTMAX signal to initiate new snapshot */ 458 | void snapshot_handler (int signo, siginfo_t * info, void * extra) 459 | { 460 | (void)signo; 461 | (void)extra; 462 | 463 | static char * __cmd = NULL; 464 | static char __proc_entry[MALLOC_CMD_PAD]; 465 | 466 | static struct itimerspec it = { 467 | .it_value.tv_sec = 0, 468 | .it_value.tv_nsec = 0, 469 | .it_interval.tv_sec = 0, 470 | .it_interval.tv_nsec = 0, /* One shot mode */ 471 | }; 472 | 473 | timer_t timer = *((timer_t *)(info->si_value.sival_ptr)); 474 | int i; 475 | 476 | 477 | /* Set next activation */ 478 | it.it_value.tv_nsec = MS_TO_NS(snap_period_ms); 479 | 480 | /* Should happen only once */ 481 | if (!__cmd) 482 | __cmd = (char *)malloc(strlen(outdir) + MALLOC_CMD_PAD); 483 | 484 | /* Send SIGSTOP to all the children (skip in async mode) */ 485 | for (i = 0; i < bm_count && !flag_async; ++i) { 486 | kill(pids[i], SIGSTOP); 487 | } 488 | 489 | /* Skip all of this in mimic mode */ 490 | if(!flag_mimic) { 491 | /* Ask the module to acquire a new snapshot.*/ 492 | acquire_new_snapshot(); 493 | 494 | /* Unless we are in transparent mode, save cache dump 495 | * to file right away */ 496 | if (!flag_transparent) { 497 | sprintf(__cmd, "%s/cachedump%d.csv", outdir, snapshots); 498 | 499 | /* In non-transparent mode, no autoincrement 500 | * is selected in the kernel, so we always 501 | * read the first buffer. */ 502 | read_cache_to_file(__cmd, 0); 503 | } 504 | } 505 | 506 | /* Acquire maps files if layout acquisition is selected */ 507 | if (flag_bm_layout) { 508 | /* Initiate a /proc/pid/maps dump to file */ 509 | for (i = 0; i < bm_count; ++i) { 510 | sprintf(__cmd, "%s/%d-%d.txt", outdir, pids[i], snapshots); 511 | sprintf(__proc_entry, "/proc/%d/maps", pids[i]); 512 | copy_file(__proc_entry, __cmd); 513 | } 514 | } 515 | 516 | /* Resume all the children with SIGCONT (skip in async mode) */ 517 | for (i = 0; i < bm_count && !flag_async; ++i) { 518 | kill(pids[i], SIGCONT); 519 | } 520 | 521 | /* Keep track of the total number of snapshots acquired so far */ 522 | ++snapshots; 523 | 524 | /* If this is snapshot #1, then just acquire another snapshot 525 | * right away and that will be it. */ 526 | if (flag_overhead) { 527 | if (snapshots == 1) { 528 | /* Set timer's next activation */ 529 | timer_settime(timer, 0, &it, NULL); 530 | } 531 | if (snapshots == 2) { 532 | snapshot_handler (signo, info, extra); 533 | } 534 | } else { 535 | /* Set timer's next activation */ 536 | timer_settime(timer, 0, &it, NULL); 537 | } 538 | 539 | } 540 | 541 | /* Handler for SIGRTMAX-1 signal to initiate new snapshot */ 542 | void ext_snapshot_handler (int signo, siginfo_t * info, void * extra) 543 | { 544 | (void)signo; 545 | (void)extra; 546 | 547 | static char * __cmd = NULL; 548 | static char __proc_entry[MALLOC_CMD_PAD]; 549 | 550 | int i; 551 | 552 | /* Should happen only once */ 553 | if (!__cmd) 554 | __cmd = (char *)malloc(strlen(outdir) + MALLOC_CMD_PAD); 555 | 556 | /* Send SIGSTOP to all the children (skip in async mode) */ 557 | for (i = 0; i < bm_count && !flag_async; ++i) { 558 | kill(pids[i], SIGSTOP); 559 | } 560 | 561 | /* Skip all of this in mimic mode */ 562 | if(!flag_mimic) { 563 | /* Ask the module to acquire a new snapshot.*/ 564 | acquire_new_snapshot(); 565 | 566 | /* Unless we are in transparent mode, save cache dump 567 | * to file right away */ 568 | if (!flag_transparent) { 569 | sprintf(__cmd, "%s/cachedump%d.csv", outdir, snapshots); 570 | 571 | /* In non-transparent mode, no autoincrement 572 | * is selected in the kernel, so we always 573 | * read the first buffer. */ 574 | read_cache_to_file(__cmd, 0); 575 | } 576 | } 577 | 578 | /* Acquire maps files if layout acquisition is selected */ 579 | if (flag_bm_layout) { 580 | /* Initiate a /proc/pid/maps dump to file */ 581 | for (i = 0; i < bm_count; ++i) { 582 | sprintf(__cmd, "%s/%d-%d.txt", outdir, pids[i], snapshots); 583 | sprintf(__proc_entry, "/proc/%d/maps", pids[i]); 584 | copy_file(__proc_entry, __cmd); 585 | } 586 | } 587 | 588 | /* Resume all the children with SIGCONT (skip in async mode) */ 589 | for (i = 0; i < bm_count && !flag_async; ++i) { 590 | kill(pids[i], SIGCONT); 591 | } 592 | 593 | /* Keep track of the total number of snapshots acquired so far */ 594 | ++snapshots; 595 | 596 | } 597 | 598 | 599 | /* Function to complete execution */ 600 | void wrap_up (void) 601 | { 602 | char * pathname; 603 | int pids_fd, len, i; 604 | 605 | pathname = (char *)malloc(strlen(outdir) + MALLOC_CMD_PAD); 606 | 607 | /* If we are running in transparent mode, now it's the time to 608 | * dump all the snapshots. */ 609 | if (flag_transparent) { 610 | int dumpcache_fd = open_mod(); 611 | int retval; 612 | 613 | retval = ioctl(dumpcache_fd, DUMPCACHE_CMD_CONFIG, DUMPCACHE_CMD_GETBUF_SHIFT); 614 | if (retval < 0) { 615 | perror("Unable to retrieve current buffer index from shutter"); 616 | exit(EXIT_FAILURE); 617 | } 618 | 619 | /* The read_cache_to_file function will re-open this 620 | * file. So close it for now. */ 621 | close(dumpcache_fd); 622 | 623 | /* If the number of snapshots is in mismatch, 624 | * something is wrong! */ 625 | if (retval != snapshots) 626 | fprintf(stderr, "WARNING: Number of snapshots does not match the expected" 627 | "value. Possible overflow?\n"); 628 | 629 | for (i = 0; i < retval; ++i) { 630 | sprintf(pathname, "%s/cachedump%d.csv", outdir, i); 631 | read_cache_to_file(pathname, i); 632 | } 633 | 634 | } 635 | 636 | /* Now create pids file with metadata about the acquisition */ 637 | sprintf(pathname, "%s/pids.txt", outdir); 638 | pids_fd = open(pathname, O_CREAT | O_TRUNC | O_RDWR, 0700); 639 | 640 | if (pids_fd < 0) { 641 | perror("Unable to write pids file"); 642 | exit(EXIT_FAILURE); 643 | } 644 | 645 | /* Write out number of snapshots */ 646 | len = sprintf(pathname, "%d\n", snapshots); 647 | write(pids_fd, pathname, len); 648 | 649 | /* Write out PID of parent process */ 650 | len = sprintf(pathname, "%d\n", getpid()); 651 | write(pids_fd, pathname, len); 652 | 653 | for (i = 0; i < bm_count; ++i) { 654 | len = sprintf(pathname, "%d\n", pids[i]); 655 | write(pids_fd, pathname, len); 656 | } 657 | 658 | close(pids_fd); 659 | free(pathname); 660 | } 661 | 662 | /* Set real-time SCHED_FIFO scheduler with given priority */ 663 | void set_realtime(int prio) 664 | { 665 | struct sched_param sp; 666 | 667 | /* Initialize parameters */ 668 | memset(&sp, 0, sizeof(struct sched_param)); 669 | sp.sched_priority = prio; 670 | 671 | /* Attempt to set the scheduler for current process */ 672 | if (sched_setscheduler(0, SCHED_FIFO, &sp) < 0) { 673 | perror("Unable to set SCHED_FIFO scheduler"); 674 | exit(EXIT_FAILURE); 675 | } 676 | 677 | /* Set CPU affinity if isolate flag specified */ 678 | if (flag_isol) { 679 | cpu_set_t set; 680 | CPU_ZERO(&set); 681 | 682 | /* default to CPU x for parent */ 683 | CPU_SET(PARENT_CPU, &set); 684 | 685 | if (sched_setaffinity(getpid(), sizeof(set), &set) == -1) { 686 | perror("Unable to set CPU affinity."); 687 | exit(EXIT_FAILURE); 688 | } 689 | 690 | } 691 | } 692 | 693 | /* Only change RT prio of calling process */ 694 | void change_rt_prio(int prio) 695 | { 696 | struct sched_param sp; 697 | 698 | /* Initialize attributes */ 699 | memset(&sp, 0, sizeof(struct sched_param)); 700 | sp.sched_priority = prio; 701 | 702 | if(sched_setparam(0, &sp) < 0) { 703 | perror("Unable to set new RT priority"); 704 | exit(EXIT_FAILURE); 705 | } 706 | 707 | /* Set CPU affinity if isolate flag specified */ 708 | if (flag_isol) { 709 | cpu_set_t set; 710 | int i, nprocs; 711 | 712 | nprocs = get_nprocs(); 713 | 714 | CPU_ZERO(&set); 715 | 716 | /* default to CPU x for parent */ 717 | for (i = 0; i < nprocs; ++i) { 718 | if (i != PARENT_CPU) 719 | CPU_SET(i, &set); 720 | } 721 | 722 | if (sched_setaffinity(getpid(), sizeof(set), &set) == -1) { 723 | perror("Unable to set CPU affinity."); 724 | exit(EXIT_FAILURE); 725 | } 726 | 727 | } 728 | 729 | } 730 | 731 | /* Set non-real-time SCHED_OTHER scheduler */ 732 | void set_non_realtime(void) 733 | { 734 | struct sched_param sp; 735 | 736 | /* Initialize parameters */ 737 | memset(&sp, 0, sizeof(struct sched_param)); 738 | 739 | /* Attempt to set the scheduler for current process */ 740 | if (sched_setscheduler(0, SCHED_OTHER, &sp) < 0) { 741 | perror("Unable to set SCHED_OTHER scheduler"); 742 | exit(EXIT_FAILURE); 743 | } 744 | } 745 | 746 | /* Install periodic snapshot handler and wait for completion using signals */ 747 | void wait_completion(void) 748 | { 749 | sigset_t waitmask; 750 | struct sigaction chld_sa; 751 | struct sigaction timer_sa; 752 | struct sigaction ext_sa; 753 | 754 | struct sigevent ev; 755 | struct itimerspec it; 756 | timer_t timer; 757 | 758 | /* Use RT POSIX extension */ 759 | chld_sa.sa_flags = SA_SIGINFO; 760 | chld_sa.sa_sigaction = proc_exit_handler; 761 | sigemptyset(&chld_sa.sa_mask); 762 | sigaddset(&chld_sa.sa_mask, SIGCHLD); 763 | 764 | /* Install SIGCHLD signal handler */ 765 | sigaction(SIGCHLD, &chld_sa, NULL); 766 | 767 | /* Setup timer */ 768 | timer_sa.sa_flags = SA_SIGINFO; 769 | timer_sa.sa_sigaction = snapshot_handler; 770 | sigemptyset(&timer_sa.sa_mask); 771 | sigaddset(&timer_sa.sa_mask, SIGRTMAX); 772 | sigaddset(&timer_sa.sa_mask, SIGRTMAX-1); 773 | 774 | /* Install SIGRTMAX signal handler */ 775 | sigaction(SIGRTMAX, &timer_sa, NULL); 776 | 777 | /* Setup signal hanlder for external trigger */ 778 | ext_sa.sa_flags = SA_SIGINFO; 779 | ext_sa.sa_sigaction = ext_snapshot_handler; 780 | sigemptyset(&ext_sa.sa_mask); 781 | sigaddset(&ext_sa.sa_mask, SIGRTMAX); 782 | sigaddset(&ext_sa.sa_mask, SIGRTMAX-1); 783 | 784 | /* Install SIGRTMAX signal handler */ 785 | sigaction(SIGRTMAX-1, &ext_sa, NULL); 786 | 787 | /* Timer creation */ 788 | memset(&ev, 0, sizeof(ev)); 789 | ev.sigev_notify = SIGEV_SIGNAL; 790 | ev.sigev_signo = SIGRTMAX; 791 | 792 | /* Pass a reference to the timer. */ 793 | ev.sigev_value.sival_ptr = (void *)&timer; 794 | 795 | timer_create(CLOCK_REALTIME, &ev, &timer); 796 | 797 | memset(&it, 0, sizeof(it)); 798 | it.it_value.tv_sec = 0; 799 | it.it_value.tv_nsec = 1; /* Start immediately */ 800 | it.it_interval.tv_sec = 0; 801 | it.it_interval.tv_nsec = 0; /* One shot mode for now. */ 802 | 803 | 804 | /* Start timer only if we are operating in periodic mode */ 805 | if (flag_periodic) { 806 | timer_settime(timer, 0, &it, NULL); 807 | } 808 | 809 | printf("Setup completed!\n"); 810 | 811 | /* Wait for any signal */ 812 | sigemptyset(&waitmask); 813 | while(!done){ 814 | sigsuspend(&waitmask); 815 | } 816 | 817 | timer_delete(timer); 818 | } 819 | 820 | 821 | /* Entry function to interface with the kernel module via the proc interface */ 822 | void read_cache_to_file(char * filename, int index) { 823 | int outfile; 824 | int dumpcache_fd; 825 | int bytes_to_write = 0; 826 | struct cache_sample * cache_contents; 827 | 828 | char csv_file_buf[WRITE_SIZE + 10*CSV_LINE_SIZE]; 829 | int cache_set_idx, cache_line_idx; 830 | 831 | cache_contents = NULL; 832 | 833 | if (((outfile = open(filename, O_CREAT | O_WRONLY | O_SYNC | O_TRUNC, 0666)) < 0)) { 834 | perror("Failed to open outfile"); 835 | exit(EXIT_FAILURE); 836 | } 837 | 838 | if (!cache_contents) { 839 | cache_contents = (struct cache_sample *)malloc(sizeof(struct cache_sample)); 840 | } 841 | 842 | dumpcache_fd = open_mod(); 843 | 844 | if (flag_transparent) { 845 | /* Make sure to ask the kernel for the buffer at the 846 | * specific index. */ 847 | int retval; 848 | unsigned long cmd = DUMPCACHE_CMD_SETBUF_SHIFT; 849 | 850 | cmd |= DUMPCACHE_CMD_VALUE(index); 851 | 852 | retval = ioctl(dumpcache_fd, DUMPCACHE_CMD_CONFIG, cmd); 853 | if (retval < 0) { 854 | perror("Unable to retrieve current buffer index from shutter"); 855 | exit(EXIT_FAILURE); 856 | } 857 | 858 | } 859 | 860 | if (read(dumpcache_fd, cache_contents, sizeof(struct cache_sample)) < 0) { 861 | perror("Failed to read from proc file"); 862 | exit(EXIT_FAILURE); 863 | } 864 | 865 | for (cache_set_idx = 0; cache_set_idx < NUM_CACHESETS; cache_set_idx++) { 866 | for (cache_line_idx = 0; cache_line_idx < NUM_CACHELINES; cache_line_idx++) { 867 | bytes_to_write += sprintf(csv_file_buf + bytes_to_write, 868 | "%05d,0x%012lx\n", 869 | cache_contents->sets[cache_set_idx] 870 | .cachelines[cache_line_idx].pid, 871 | cache_contents->sets[cache_set_idx] 872 | .cachelines[cache_line_idx].addr); 873 | 874 | /* Flush out pending data */ 875 | if (bytes_to_write >= WRITE_SIZE){ 876 | if (write(outfile, csv_file_buf, bytes_to_write) == -1) { 877 | perror("Failed to write to outfile"); 878 | } 879 | bytes_to_write = 0; 880 | } 881 | } 882 | } 883 | 884 | /* Flush out leftover buffer data */ 885 | if (bytes_to_write){ 886 | if (write(outfile, csv_file_buf, bytes_to_write) == -1) { 887 | perror("Failed to write to outfile"); 888 | } 889 | } 890 | 891 | close(outfile); 892 | close(dumpcache_fd); 893 | } 894 | --------------------------------------------------------------------------------