disksim负载合成

源码理解

从一开始的disksim_synthio.c文件中的相关函数入手(这是根据之前的源码理解这部分的代码和负载合成有关):
首先根据函数名字猜测

1
synthio_generate_io_activity

函数是不是和负载合成的有关,查看该函数的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void synthio_generate_io_activity (process *procp)
{
synthio_generator *gen;

gen = (synthio_generator *) procp->space;

// fprintf (stderr, "simtime %f, endtime %f, iocnt %d, endiocnt %d\n", simtime, synthio_endtime, synthio_iocnt, synthio_endiocnt);

synthio_iocnt++;
if (synthio_generatenextio(gen)) {
synthio_appendio(procp, gen->pendio);
} else {
disksim_simstop();
}
}

可以看到该函数调用了其他两个相关的函数synthio_generatenextio和synthio_appendio前一个是生成一个IO请求,后一个是将生成的IO请求挂到对应的队列上去之后处理。
查看synthio_generatenextio(也在disksim_synthio.c文件)的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static int synthio_generatenextio (synthio_generator *gen)
{
double type;
double reqclass;
int blkno;
ioreq_event *tmp;

if ((simtime >= synthio_endtime) || (synthio_iocnt >= synthio_endiocnt))
return 0;

tmp = gen->pendio;
if (gen->tracefile) {
gen->pendio =
(ioreq_event *)iotrace_get_ioreq_event(gen->tracefile, disksim->traceformat, tmp);

if (gen->pendio) {
tmp->cause = gen->number;
/* tmp->time = 0; */
} else {
fprintf(stderr, "Returning NULL event in synthio_generatenextio\n");
return 0;
}
return 1;
}

可以该函数果然是调用iotrace_get_ioreq_event产生下一个io请求
查看synthio_appendio函数(也在disksim_synthio.c文件)定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
static void synthio_appendio (process *procp, ioreq_event *tmp)
{
synthio_generator *gen = (synthio_generator *) procp->space;
sleep_event *limittmp = NULL;
event *prev = NULL;
sleep_event *newsleep = NULL;
sleep_event *callsleep = NULL;
sleep_event *retsleep = NULL;
ioreq_event *new;
int blocksize;
int gennum;

blocksize = gen->blocksize;
gennum = gen->number;
new = (ioreq_event *) getfromextraq();
new->type = IOREQ_EVENT;
new->time = tmp->time;
new->devno = tmp->devno;
new->blkno = tmp->blkno * blocksize;
new->bcount = tmp->bcount * blocksize;
new->flags = tmp->flags;
new->cause = tmp->cause;
new->opid = (synthio_endiocnt * gennum) + synthio_iocnt;
.......根据不同的类型将new加入到队列中去

////output this entry-zhoujie,print new->time!=0
//if(new->time!=0.000000){
//fprintf(outputfile,"New request %d, time %f, devno %d, blkno %d, bcount %d, flags %x\n", synthio_iocnt, new->time, new->devno, new->blkno, new->bcount, new->flags);
//fflush(outputfile);
//}
//fprintf (stderr, "New request %d, time %f, devno %d, blkno %d, bcount %d, flags %x\n", synthio_iocnt, new->time, new->devno, new->blkno, new->bcount, new->flags);
}

该函数的定义过于长,因此删除一部分的代码,可以看到这个代码中源码作者在这里有尝试输出部分的合成请求信息用于调试,但是这部分的New request不是我们想要的合成负载,这里面的负载还包含了一部分的模拟CPU中断的请求,CPU调度的,不可作为外部负载提取。因此利用GDB的bt命令重新查看函数的调用关系:

1
2
3
4
5
6
7
8
9
graph TD
disksim_run_simulation-->disksim_simulate_event
disksim_simulate_event-->pf_internal_event
pf_internal_event-->pf_handle_cpu_event
pf_handle_cpu_event-->pf_handle_event
pf_handle_event-->pf_handle_synthio_event
pf_handle_synthio_event-->synthio_generate_io_activity
synthio_generate_io_activity-->|one|synthio_generatenextio
synthio_generate_io_activity-->|two|synthio_appendio

为了方便理解先从disksim_run_simulation函数开始理解

1
2
3
4
5
6
7
8
9
10
11
void disksim_run_simulation ()
{
int event_count = 0;
// DISKSIM_srand48(1000003);
while (disksim->stop_sim == FALSE) {
disksim_simulate_event(event_count);
// printf("disksim_run_simulation: event %d\n", event_count);
event_count++;
}
printf("disksim_run_simulation(): simulated %d events\n", event_count);
}

开始看到该函数是反复调用disksim_simulate_event函数,查看disksim_simulate_event的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
void disksim_simulate_event (int num)
{
event *curr;

if ((curr = getnextevent()) == NULL) {
disksim_simstop ();
}
else {

switch(disksim->trace_mode) {
case DISKSIM_NONE:
case DISKSIM_MASTER:
////test point-zhoujie
//fprintf(stdout, "*** DEBUG TRACE\t%f\t%d\t%d\n", simtime, curr->type, num);
//fflush(stdout);
//fprintf(outputfile, "*** DEBUG TRACE\t%f\t%d\t%d\n", simtime, curr->type, num);
//fflush(outputfile);
break;

case DISKSIM_SLAVE:
break;
}

simtime = curr->time;

if (curr->type == INTR_EVENT) {
intr_acknowledge (curr);
}
else if ((curr->type >= IO_MIN_EVENT) && (curr->type <= IO_MAX_EVENT)) {
io_internal_event ((ioreq_event *)curr);
}
else if ((curr->type >= PF_MIN_EVENT) && (curr->type <= PF_MAX_EVENT)) {
pf_internal_event(curr);
}
else if (curr->type == TIMER_EXPIRED) {
timer_event *timeout = (timer_event *) curr;
(*timeout->func) (timeout);
}
else if ((curr->type >= MEMS_MIN_EVENT)
&& (curr->type <= MEMS_MAX_EVENT)) {
io_internal_event ((ioreq_event *)curr);
}
else if (curr->type == CHECKPOINT) {
if (disksim->checkpoint_interval) {
disksim_register_checkpoint(simtime + disksim->checkpoint_interval);
}
disksim_checkpoint (disksim->checkpointfilename);
}
else if (curr->type == STOP_SIM) {
disksim_simstop ();
}
else if (curr->type == EXIT_DISKSIM) {
exit (0);
}
else {
fprintf(stderr, "Unrecognized event in simulate: %d\n", curr->type);
exit(1);
}


#ifdef FDEBUG
fprintf (outputfile, "Event handled, going for next\n");
fflush (outputfile);
#endif
}


}

这个函数最主要的是根据当前的curr->type的类型调用不同函数的处理,curr结构体中包含了一部分和IO请求相关的信息,注意其中的代码片段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
if (curr->type == INTR_EVENT) {
intr_acknowledge (curr);
}
else if ((curr->type >= IO_MIN_EVENT) && (curr->type <= IO_MAX_EVENT)) {
io_internal_event ((ioreq_event *)curr);
}
else if ((curr->type >= PF_MIN_EVENT) && (curr->type <= PF_MAX_EVENT)) {
pf_internal_event(curr);
}
else if (curr->type == TIMER_EXPIRED) {
timer_event *timeout = (timer_event *) curr;
(*timeout->func) (timeout);
}
else if ((curr->type >= MEMS_MIN_EVENT)
&& (curr->type <= MEMS_MAX_EVENT)) {
io_internal_event ((ioreq_event *)curr);
}
else if (curr->type == CHECKPOINT) {
if (disksim->checkpoint_interval) {
disksim_register_checkpoint(simtime + disksim->checkpoint_interval);
}
disksim_checkpoint (disksim->checkpointfilename);
}
else if (curr->type == STOP_SIM) {
disksim_simstop ();
}
else if (curr->type == EXIT_DISKSIM) {
exit (0);
}
else {
fprintf(stderr, "Unrecognized event in simulate: %d\n", curr->type);
exit(1);
}

负载合成是在第2个判断成立下执行的,调用了 pf_internal_event,同时注意到上一个条件貌似就是在执行IO请求。反过来考虑如果当前的负载合成关闭,输入外部负载也会调用这个条件处理IO请求,输入的参数就是外部负载,同理合成的负载也会进入该函数,输入的参数就是我们想要的合成负载数据。
因此进入io_internal_event ((ioreq_event *)curr)该函数查看相关的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
void io_internal_event(ioreq_event *curr)
{
ASSERT(curr != NULL);
/*
fprintf (outputfile, "%f: io_internal_event entered with event type %d, %f\n", curr->time, curr->type, simtime);
*/
switch (curr->type) {
case IO_REQUEST_ARRIVE:
iodriver_request(0, curr);
break;

case IO_ACCESS_ARRIVE:
iodriver_schedule(0, curr);
break;

case IO_INTERRUPT_ARRIVE:
iodriver_interrupt_arrive(0, (intr_event *) curr);
break;

case IO_RESPOND_TO_DEVICE:
iodriver_respond_to_device(0, (intr_event *) curr->tempptr1);
addtoextraq((event *) curr);
break;

case IO_ACCESS_COMPLETE:
iodriver_access_complete(0, (intr_event *) curr->tempptr1);
addtoextraq((event *) curr);
break;

case IO_INTERRUPT_COMPLETE:
iodriver_interrupt_complete(0, (intr_event *) curr);
break;

case DEVICE_OVERHEAD_COMPLETE:
case DEVICE_ACCESS_COMPLETE:
case DEVICE_DATA_TRANSFER_COMPLETE:
case DEVICE_PREPARE_FOR_DATA_TRANSFER:
case DEVICE_BUFFER_SEEKDONE:
case DEVICE_BUFFER_TRACKACC_DONE:
case DEVICE_BUFFER_SECTOR_DONE:
case DEVICE_GOT_REMAPPED_SECTOR:
case DEVICE_GOTO_REMAPPED_SECTOR:
case MEMS_SLED_SCHEDULE:
case MEMS_SLED_SEEK:
case MEMS_SLED_SERVO:
case MEMS_SLED_DATA:
case MEMS_SLED_UPDATE:
case MEMS_BUS_INITIATE:
case MEMS_BUS_TRANSFER:
case MEMS_BUS_UPDATE:
device_event_arrive(curr);
break;

case BUS_OWNERSHIP_GRANTED:
case BUS_DELAY_COMPLETE:
bus_event_arrive(curr);
break;

case CONTROLLER_DATA_TRANSFER_COMPLETE:
controller_event_arrive(curr->tempint2, curr);
break;

case TIMESTAMP_LOGORG:
logorg_timestamp(curr);
break;

case IO_TRACE_REQUEST_START:
iodriver_trace_request_start(0, curr);
break;

default:
fprintf(stderr, "Unknown event type passed to io_internal_events\n");
exit(1);
}
/*
fprintf (outputfile, "Leaving io_internal_event\n");
*/
}

函数是根据不同的类型模拟控制器处理请求,可以看到代码段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
case IO_REQUEST_ARRIVE:
iodriver_request(0, curr);
break;

case IO_ACCESS_ARRIVE:
iodriver_schedule(0, curr);
break;

case IO_INTERRUPT_ARRIVE:
iodriver_interrupt_arrive(0, (intr_event *) curr);
break;

case IO_RESPOND_TO_DEVICE:
iodriver_respond_to_device(0, (intr_event *) curr->tempptr1);
addtoextraq((event *) curr);
break;

case IO_ACCESS_COMPLETE:
iodriver_access_complete(0, (intr_event *) curr->tempptr1);
addtoextraq((event *) curr);
break;

case IO_INTERRUPT_COMPLETE:
iodriver_interrupt_complete(0, (intr_event *) curr);
break;

其中IO_REQUEST_ARRIVE就是我们想要的外部负载处理的部分,这里调用了 iodriver_request(0, curr)函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
event * iodriver_request (int iodriverno, ioreq_event *curr)
{
ioreq_event *temp = NULL;
ioreq_event *ret = NULL;
ioreq_event *retlist = NULL;
int numreqs;
/*
printf ("Entered iodriver_request - simtime %f, devno %d, blkno %d, cause %d\n", simtime, curr->devno, curr->blkno, curr->cause);
fprintf (outputfile, "Entered iodriver_request - simtime %f, devno %d, blkno %d, cause %d\n", simtime, curr->devno, curr->blkno, curr->cause);
fprintf (stderr, "Entered iodriver_request - simtime %f, devno %d, blkno %d, cause %d\n", simtime, curr->devno, curr->blkno, curr->cause);
*/
if (outios) {
fprintf(outios, "%.6f\t%d\t%d\t%d\t%x\n", simtime, curr->devno, curr->blkno, curr->bcount, curr->flags);
}
//test for output
fprintf(outputfile, "TheTest %.6f\t%d\t%d\t%d\t%x\n", simtime, curr->devno, curr->blkno, curr->bcount, curr->flags);
fflush(outputfile);

#if 0
fprintf (stderr, "Entered iodriver_request - simtime %f, devno %d, blkno %d, cause %d\n", simtime, curr->devno, curr->blkno, curr->cause);
#endif

/* add to the overall queue to start tracking */
ret = ioreq_copy (curr);
ioqueue_add_new_request (overallqueue, ret);
ret = NULL;

disksim->totalreqs++;
if ((disksim->checkpoint_iocnt > 0) && ((disksim->totalreqs % disksim->checkpoint_iocnt) == 0)) {
disksim_register_checkpoint (simtime);
}
if (disksim->totalreqs == disksim->warmup_iocnt) {
warmuptime = simtime;
resetstats();
}
numreqs = logorg_maprequest(sysorgs, numsysorgs, curr);
temp = curr->next;
for (; numreqs>0; numreqs--) {
/* Request list size must match numreqs */
ASSERT(curr != NULL);
curr->next = NULL;
if ((iodrivers[iodriverno]->consttime == IODRIVER_TRACED_QUEUE_TIMES) || (iodrivers[iodriverno]->consttime == IODRIVER_TRACED_BOTH_TIMES)) {
ret = ioreq_copy(curr);
ret->time = simtime + (double) ret->tempint1 / (double) 1000;
ret->type = IO_TRACE_REQUEST_START;
addtointq((event *) ret);
ret = NULL;
if ((curr->slotno == 1) && (ioqueue_get_number_in_queue(iodrivers[iodriverno]->devices[(curr->devno)].queue) == 0)) {
iodrivers[(iodriverno)]->devices[(curr->devno)].flag = 2;
iodrivers[(iodriverno)]->devices[(curr->devno)].lastevent = simtime;
}
}
ret = handle_new_request(iodrivers[iodriverno], curr);

if ((ret) && (iodrivers[iodriverno]->type == STANDALONE) && (ret->time == 0.0)) {
ret->type = IO_ACCESS_ARRIVE;
ret->time = simtime;
iodriver_schedule(iodriverno, ret);
} else if (ret) {
ret->type = IO_ACCESS_ARRIVE;
ret->next = retlist;
ret->prev = NULL;
retlist = ret;
}
curr = temp;
temp = (temp) ? temp->next : NULL;
}
if (iodrivers[iodriverno]->type == STANDALONE) {
while (retlist) {
ret = retlist;
retlist = ret->next;
ret->next = NULL;
ret->time += simtime;
addtointq((event *) ret);
}
}
/*
fprintf (outputfile, "leaving iodriver_request: retlist %p\n", retlist);
*/
return((event *) retlist);
}

可以看到这个函数的注释部分有尝试输出负载:

1
2
3
if (outios) {
fprintf(outios, "%.6f\t%d\t%d\t%d\t%x\n", simtime, curr->devno, curr->blkno, curr->bcount, curr->flags);
}

但是这个输出是文件指针outios是打开的,简单起见我们将负载直接输出到输出文件XXX.outv中,添加自己的代码:

1
2
3
//test for output
fprintf(outputfile, "TheTest %.6f\t%d\t%d\t%d\t%x\n", simtime, curr->devno, curr->blkno, curr->bcount, curr->flags);
fflush(outputfile);

这里TheTest是为了后面的文本提取做的标记。
之后再编译运行相关配置文件得到XXX.outv中出现了我们想要的Trace:

1
2
3
4
5
6
7
8
9
10
11
12
13
Initialization complete
TheTest 0.000000 0 228392 8 3
TheTest 0.000000 0 1420312 16 2
TheTest 0.000000 0 1582736 8 2
TheTest 0.000000 0 899784 8 2
TheTest 0.000000 0 1745352 8 2
TheTest 13.520967 0 261752 8 20000002
TheTest 28.927205 0 929536 24 20000003
TheTest 43.287041 0 1395616 8 20000002
TheTest 53.535361 0 1561888 8 20000002
TheTest 73.333340 0 1710912 8 20000002
TheTest 102.314824 0 1691648 8 2
TheTest 118.037274 0 960856 40 20000003

关于后面的标志位这里需要再提一下:

1
2
3
4
5
6
7
8
最后合成的:trace 每条请求如下:
4.911651 0 16350 1 20000003
有5个值,分别是
请求发起的时间 4.911651
访问的设备号 0
访问的块号 16350
该请求读取的块数目 1
标志位 20000003

标志位里保存了trace的相关信息 ,包括读/写 以及是否local request 等
标志信息的定义在disksim_reqflags.h里,如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
#define DISKSIM_WRITE        0x00000000
#define DISKSIM_READ 0x00000001
#define DISKSIM_TIME_CRITICAL 0x00000002
#define DISKSIM_TIME_LIMITED 0x00000004
#define DISKSIM_TIMED_OUT 0x00000008
#define DISKSIM_HALF_OUT 0x00000010
#define DISKSIM_MAPPED 0x00000020
#define DISKSIM_READ_AFTR_WRITE 0x00000040
#define DISKSIM_SYNC 0x00000080
#define DISKSIM_ASYNC 0x00000100
#define DISKSIM_IO_FLAG_PAGEIO 0x00000200
#define DISKSIM_SEQ 0x40000000
#define DISKSIM_LOCAL 0x20000000
1
2
#define DISKSIM_BATCH_COMPLETE  0x80000000
所以上面那条请求的标志位表示了该请求是读请求,time_critical ,Local的请求

通过编写一个小的统计程序来收集生成的trace的信息,证明了生成的trace是满足我们要求的。
但是我们用disksim+flashsim的负载用的是ascii格式,这个标志位在之后需要进行修改:0表示写请求,1表示为读请求。

针对合成的负载提取有用的信息,写了一个shell脚本可以快速提取文件中信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/bash
########################
# filename:extrac-trace.sh
# Usage:./extrac-trace.sh
# author:Zhoujie
# date:2017-10-31
#######################
read -p "Enter the disksim generate XXXX.outv:" inputfile
read -p "Enter the result filename what you want to call:" savefile
echo -e "Touch tempfile to save vaild-data from $inputfile..."
touch tempfile
echo "read vaild-information from $inputfile to tempfile..."
grep "TheTest" $inputfile > tempfile
echo "remove 'TheTest' from data and save result in $savefile..."
awk -vOFS="\t" '{print $2,$3,$4,$5,$6}' tempfile >$savefile
rm tempfile
echo "finish the result extrac!"

但是提取到的Trace的标位还没有修改完成,因此借助matlab快速的数据处理整合为符合条件的ascii类型的负载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
clc
clear all;
trace_filename='C5';%配置输入的trace文件
result_filename='C5.txt';
[req_arrive,device_num,req_sec_start,req_sec_size,req_type]=textread(trace_filename,'%f%d%d%d%d');
% %读取trace文件中的五个参数(请求到达时间,设备号,请求扇区号起始位置,请求扇区大小,请求操作类型)

L=length(req_type);
t=zeros(L,1);
% 写入数据
fp=fopen(result_filename,'a');
for index=1:L
if mod(req_type(index),10)==0
% req_type(index)=0;
t(index)=0;
else
% req_type(index)=1;
t(index)=1;
end
fprintf(fp,'%f %d %d %d %d\n',req_arrive(index),device_num(index),req_sec_start(index),req_sec_size(index),t(index));
end
fclose(fp);
% 清除中间变量
clear all

到此为止,将XXX.txt放入到Disksim+flashsim的仿真器上就能跑了。读写比例自己改配置文件即可。

热评文章