| 1 |
module cn.kuehne.dstress.log; |
|---|
| 2 |
|
|---|
| 3 |
private import std.string; |
|---|
| 4 |
private import std.conv; |
|---|
| 5 |
private import std.stdio; |
|---|
| 6 |
private import std.stream; |
|---|
| 7 |
private import std.file; |
|---|
| 8 |
private import std.c.stdlib; |
|---|
| 9 |
private import std.date; |
|---|
| 10 |
|
|---|
| 11 |
static char[][] TORTURE_FLAGS = [ |
|---|
| 12 |
/* 0 args */ |
|---|
| 13 |
"", |
|---|
| 14 |
|
|---|
| 15 |
/* 1 args */ |
|---|
| 16 |
"-g", |
|---|
| 17 |
"-inline", |
|---|
| 18 |
"-fPIC", |
|---|
| 19 |
"-O", |
|---|
| 20 |
"-release", |
|---|
| 21 |
|
|---|
| 22 |
/* 2 args */ |
|---|
| 23 |
"-g -inline", |
|---|
| 24 |
"-g -fPIC", |
|---|
| 25 |
"-g -O", |
|---|
| 26 |
"-g -release", |
|---|
| 27 |
"-inline -fPIC", |
|---|
| 28 |
"-inline -O", |
|---|
| 29 |
"-inline -release", |
|---|
| 30 |
"-fPIC -O", |
|---|
| 31 |
"-fPIC -release", |
|---|
| 32 |
"-O -release", |
|---|
| 33 |
|
|---|
| 34 |
/* 3 args */ |
|---|
| 35 |
"-g -inline -fPIC", |
|---|
| 36 |
"-g -inline -O", |
|---|
| 37 |
"-g -inline -release", |
|---|
| 38 |
"-g -fPIC -O", |
|---|
| 39 |
"-g -fPIC -release", |
|---|
| 40 |
"-g -O -release", |
|---|
| 41 |
"-inline -fPIC -O", |
|---|
| 42 |
"-inline -fPIC -release", |
|---|
| 43 |
"-inline -O -release", |
|---|
| 44 |
"-fPIC -O -release", |
|---|
| 45 |
|
|---|
| 46 |
/* 4 args */ |
|---|
| 47 |
"-g -inline -fPIC -O", |
|---|
| 48 |
"-g -inline -fPIC -release", |
|---|
| 49 |
"-g -fPIC -O -release", |
|---|
| 50 |
"-inline -fPIC -O -release", |
|---|
| 51 |
|
|---|
| 52 |
/* 5 args */ |
|---|
| 53 |
"-g -inline -fPIC -O -release", |
|---|
| 54 |
|
|---|
| 55 |
/* 4 args - ommitted */ |
|---|
| 56 |
"-g -inline -O -release" |
|---|
| 57 |
]; |
|---|
| 58 |
|
|---|
| 59 |
enum Result{ |
|---|
| 60 |
UNTESTED = 0, |
|---|
| 61 |
PASS = 1 << 2, |
|---|
| 62 |
XFAIL = 2 << 2, |
|---|
| 63 |
XPASS = 3 << 2, |
|---|
| 64 |
FAIL = 4 << 2, |
|---|
| 65 |
ERROR = 5 << 2, |
|---|
| 66 |
BASE_MASK = 7 << 2, |
|---|
| 67 |
|
|---|
| 68 |
EXT_MASK = 3, |
|---|
| 69 |
BAD_MSG = 1, |
|---|
| 70 |
BAD_GDB = 2, |
|---|
| 71 |
|
|---|
| 72 |
MAX = BAD_GDB + BASE_MASK |
|---|
| 73 |
} |
|---|
| 74 |
|
|---|
| 75 |
char[] toString(Result r){ |
|---|
| 76 |
switch(r & Result.BASE_MASK){ |
|---|
| 77 |
case Result.PASS: return "PASS"; |
|---|
| 78 |
case Result.XPASS: return "XPASS"; |
|---|
| 79 |
case Result.FAIL: return "FAIL"; |
|---|
| 80 |
case Result.XFAIL: return "XFAIL"; |
|---|
| 81 |
case Result.ERROR: return "ERROR"; |
|---|
| 82 |
case Result.UNTESTED: return "UNTESTED"; |
|---|
| 83 |
default: |
|---|
| 84 |
break; |
|---|
| 85 |
} |
|---|
| 86 |
throw new Exception(format("unhandled Result value %s", cast(int)r)); |
|---|
| 87 |
} |
|---|
| 88 |
|
|---|
| 89 |
char[] dateString(){ |
|---|
| 90 |
static char[] date; |
|---|
| 91 |
if(date is null){ |
|---|
| 92 |
auto time = getUTCtime(); |
|---|
| 93 |
auto year = YearFromTime(time); |
|---|
| 94 |
auto month = MonthFromTime(time); |
|---|
| 95 |
auto day = DateFromTime(time); |
|---|
| 96 |
date = format("%d-%02d-%02d", year, month+1, day); |
|---|
| 97 |
} |
|---|
| 98 |
return date; |
|---|
| 99 |
} |
|---|
| 100 |
|
|---|
| 101 |
char[][] unique(char[][] a){ |
|---|
| 102 |
char[][] b = a.sort; |
|---|
| 103 |
char[][] back; |
|---|
| 104 |
|
|---|
| 105 |
back ~= b[0]; |
|---|
| 106 |
|
|---|
| 107 |
size_t ii=0; |
|---|
| 108 |
for(size_t i=0; i<b.length; i++){ |
|---|
| 109 |
if(back[ii]!=b[i]){ |
|---|
| 110 |
back~=b[i]; |
|---|
| 111 |
ii++; |
|---|
| 112 |
} |
|---|
| 113 |
} |
|---|
| 114 |
|
|---|
| 115 |
return back; |
|---|
| 116 |
} |
|---|
| 117 |
|
|---|
| 118 |
private{ |
|---|
| 119 |
version(Windows){ |
|---|
| 120 |
import std.c.windows.windows; |
|---|
| 121 |
extern(Windows) BOOL GetFileTime(HANDLE hFile, LPFILETIME lpCreationTime, LPFILETIME lpLastAccessTime, LPFILETIME lpLastWriteTime); |
|---|
| 122 |
}else version(linux){ |
|---|
| 123 |
import std.c.linux.linux; |
|---|
| 124 |
version = Unix; |
|---|
| 125 |
}else version(Unix){ |
|---|
| 126 |
import std.c.unix.unix; |
|---|
| 127 |
}else{ |
|---|
| 128 |
static assert(0); |
|---|
| 129 |
} |
|---|
| 130 |
|
|---|
| 131 |
alias ulong FStime; |
|---|
| 132 |
|
|---|
| 133 |
FStime getFStime(char[] fileName){ |
|---|
| 134 |
version(Windows){ |
|---|
| 135 |
HANDLE h; |
|---|
| 136 |
|
|---|
| 137 |
if (useWfuncs){ |
|---|
| 138 |
wchar* namez = std.utf.toUTF16z(fileName); |
|---|
| 139 |
h = CreateFileW(namez,GENERIC_WRITE,0,null,OPEN_ALWAYS, |
|---|
| 140 |
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,cast(HANDLE)null); |
|---|
| 141 |
}else{ |
|---|
| 142 |
char* namez = toMBSz(fileName); |
|---|
| 143 |
h = CreateFileA(namez,GENERIC_WRITE,0,null,OPEN_ALWAYS, |
|---|
| 144 |
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,cast(HANDLE)null); |
|---|
| 145 |
} |
|---|
| 146 |
|
|---|
| 147 |
if (h == INVALID_HANDLE_VALUE) |
|---|
| 148 |
goto err; |
|---|
| 149 |
|
|---|
| 150 |
FILETIME creationTime; |
|---|
| 151 |
FILETIME accessTime; |
|---|
| 152 |
FILETIME writeTime; |
|---|
| 153 |
|
|---|
| 154 |
BOOL b = GetFileTime(h, &creationTime, &accessTime, &writeTime); |
|---|
| 155 |
if(b==1){ |
|---|
| 156 |
long modA = writeTime.dwLowDateTime; |
|---|
| 157 |
long modB = writeTime.dwHighDateTime; |
|---|
| 158 |
return modA | (modB << (writeTime.dwHighDateTime.sizeof*8)); |
|---|
| 159 |
} |
|---|
| 160 |
|
|---|
| 161 |
err: |
|---|
| 162 |
CloseHandle(h); |
|---|
| 163 |
throw new Exception("failed to query file modification : "~fileName); |
|---|
| 164 |
}else version(Unix){ |
|---|
| 165 |
char* namez = toStringz(fileName); |
|---|
| 166 |
struct_stat statbuf; |
|---|
| 167 |
|
|---|
| 168 |
if(stat(namez, &statbuf)){ |
|---|
| 169 |
throw new FileException(fileName, getErrno()); |
|---|
| 170 |
} |
|---|
| 171 |
|
|---|
| 172 |
return statbuf.st_mtime; |
|---|
| 173 |
}else{ |
|---|
| 174 |
static assert(0); |
|---|
| 175 |
} |
|---|
| 176 |
} |
|---|
| 177 |
} |
|---|
| 178 |
|
|---|
| 179 |
char[] cleanFileName(char[] file){ |
|---|
| 180 |
char[] back; |
|---|
| 181 |
bool hadSep; |
|---|
| 182 |
|
|---|
| 183 |
foreach(char c; file){ |
|---|
| 184 |
if(c == '/' || c == '\\'){ |
|---|
| 185 |
if(!hadSep){ |
|---|
| 186 |
back ~= '/'; |
|---|
| 187 |
hadSep = true; |
|---|
| 188 |
} |
|---|
| 189 |
}else{ |
|---|
| 190 |
back ~= c; |
|---|
| 191 |
hadSep = false; |
|---|
| 192 |
} |
|---|
| 193 |
} |
|---|
| 194 |
|
|---|
| 195 |
size_t start = 0; |
|---|
| 196 |
while(back[start] <= ' ' && start < back.length){ |
|---|
| 197 |
start++; |
|---|
| 198 |
} |
|---|
| 199 |
|
|---|
| 200 |
size_t end = back.length-1; |
|---|
| 201 |
while(back[end] <= ' ' && end >= start){ |
|---|
| 202 |
end--; |
|---|
| 203 |
} |
|---|
| 204 |
|
|---|
| 205 |
back = back[start .. end+1]; |
|---|
| 206 |
|
|---|
| 207 |
return back; |
|---|
| 208 |
} |
|---|
| 209 |
|
|---|
| 210 |
abstract class Test{ |
|---|
| 211 |
char[] name; |
|---|
| 212 |
char[] file; |
|---|
| 213 |
|
|---|
| 214 |
abstract Result condensed(); |
|---|
| 215 |
|
|---|
| 216 |
this(char[] file){ |
|---|
| 217 |
this.file = file; |
|---|
| 218 |
|
|---|
| 219 |
int start = rfind(file, "/"); |
|---|
| 220 |
if(start<0){ |
|---|
| 221 |
start = 0; |
|---|
| 222 |
}else{ |
|---|
| 223 |
start += 1; |
|---|
| 224 |
} |
|---|
| 225 |
|
|---|
| 226 |
int end = rfind(file, "."); |
|---|
| 227 |
if(end < start){ |
|---|
| 228 |
end = file.length; |
|---|
| 229 |
} |
|---|
| 230 |
|
|---|
| 231 |
name = file[start .. end]; |
|---|
| 232 |
} |
|---|
| 233 |
} |
|---|
| 234 |
|
|---|
| 235 |
class TortureTest : Test{ |
|---|
| 236 |
Result[] r; |
|---|
| 237 |
|
|---|
| 238 |
Result condensed(){ |
|---|
| 239 |
Result back; |
|---|
| 240 |
foreach(Result res; r){ |
|---|
| 241 |
if(res > back){ |
|---|
| 242 |
back = res; |
|---|
| 243 |
} |
|---|
| 244 |
} |
|---|
| 245 |
|
|---|
| 246 |
return back; |
|---|
| 247 |
} |
|---|
| 248 |
|
|---|
| 249 |
this(char[] file){ |
|---|
| 250 |
super(file); |
|---|
| 251 |
r.length = TORTURE_FLAGS.length; |
|---|
| 252 |
} |
|---|
| 253 |
|
|---|
| 254 |
this(char[] file, Result[] result){ |
|---|
| 255 |
if(result.length != TORTURE_FLAGS.length){ |
|---|
| 256 |
throw new Exception(format("expected %s results but got %s (%s)", TORTURE_FLAGS.length, result.length, file)); |
|---|
| 257 |
} |
|---|
| 258 |
|
|---|
| 259 |
super(file); |
|---|
| 260 |
r = result.dup; |
|---|
| 261 |
} |
|---|
| 262 |
} |
|---|
| 263 |
|
|---|
| 264 |
class Log{ |
|---|
| 265 |
TortureTest[char[]] torture; |
|---|
| 266 |
|
|---|
| 267 |
char[] id; |
|---|
| 268 |
|
|---|
| 269 |
this(char[] id){ |
|---|
| 270 |
this.id = id; |
|---|
| 271 |
} |
|---|
| 272 |
|
|---|
| 273 |
Regression[] findGlobalRegressions(Log[] logs){ |
|---|
| 274 |
Regression[] back; |
|---|
| 275 |
|
|---|
| 276 |
if(logs.length < 1){ |
|---|
| 277 |
return back; |
|---|
| 278 |
} |
|---|
| 279 |
|
|---|
| 280 |
foreach(TortureTest currentTest; torture.values){ |
|---|
| 281 |
bool hadOldData = false; |
|---|
| 282 |
Result oldResults[]; |
|---|
| 283 |
oldResults.length = TORTURE_FLAGS.length; |
|---|
| 284 |
oldResults[] = Result.MAX; |
|---|
| 285 |
|
|---|
| 286 |
foreach(Log l; logs){ |
|---|
| 287 |
TortureTest* test = currentTest.file in l.torture; |
|---|
| 288 |
if(test !is null){ |
|---|
| 289 |
hadOldData = true; |
|---|
| 290 |
foreach(size_t i, Result r; test.r){ |
|---|
| 291 |
if(r != Result.UNTESTED && r < oldResults[i]){ |
|---|
| 292 |
oldResults[i] = r; |
|---|
| 293 |
} |
|---|
| 294 |
} |
|---|
| 295 |
} |
|---|
| 296 |
} |
|---|
| 297 |
|
|---|
| 298 |
if(hadOldData){ |
|---|
| 299 |
foreach(size_t i, Result r; oldResults){ |
|---|
| 300 |
if((currentTest.r[i] != Result.UNTESTED) && (r != Result.MAX) && (currentTest.r[i] > r)){ |
|---|
| 301 |
back ~= new Regression(currentTest.name, currentTest.file, r, currentTest.r[i], TORTURE_FLAGS[i]); |
|---|
| 302 |
} |
|---|
| 303 |
} |
|---|
| 304 |
} |
|---|
| 305 |
} |
|---|
| 306 |
return back; |
|---|
| 307 |
} |
|---|
| 308 |
|
|---|
| 309 |
Regression[] findRegressions(Log oldLog){ |
|---|
| 310 |
Regression[] back; |
|---|
| 311 |
|
|---|
| 312 |
foreach(TortureTest t; torture.values){ |
|---|
| 313 |
TortureTest* oldT = t.file in oldLog.torture; |
|---|
| 314 |
|
|---|
| 315 |
if(oldT !is null){ |
|---|
| 316 |
foreach(size_t i, Result r; t.r){ |
|---|
| 317 |
if(oldT.r[i] < r && oldT.r[i]){ |
|---|
| 318 |
back ~= new Regression(t.name, t.file, oldT.r[i], r, TORTURE_FLAGS[i]); |
|---|
| 319 |
} |
|---|
| 320 |
} |
|---|
| 321 |
} |
|---|
| 322 |
} |
|---|
| 323 |
|
|---|
| 324 |
return back; |
|---|
| 325 |
} |
|---|
| 326 |
|
|---|
| 327 |
char[][] genUpdateList(char[] testRoot){ |
|---|
| 328 |
char[][] updateList; |
|---|
| 329 |
const char[][] statusList = ["compile", "nocompile", "run", "norun"]; |
|---|
| 330 |
char[] status; |
|---|
| 331 |
|
|---|
| 332 |
void list(char[] path){ |
|---|
| 333 |
if(isdir(path)){ |
|---|
| 334 |
foreach(char[] entry; listdir(path)){ |
|---|
| 335 |
if(entry.length>0 && entry[0] != '.' && entry[0] != '~'){ |
|---|
| 336 |
list(path~std.path.sep~entry); |
|---|
| 337 |
} |
|---|
| 338 |
} |
|---|
| 339 |
}else if(isfile(path)){ |
|---|
| 340 |
char[] file = path[testRoot.length + std.path.sep.length .. $]; |
|---|
| 341 |
if(!(file in torture)){ |
|---|
| 342 |
char[] output = "dstress torture-" ~ status ~ " " ~ file; |
|---|
| 343 |
updateList ~= output; |
|---|
| 344 |
} |
|---|
| 345 |
} |
|---|
| 346 |
} |
|---|
| 347 |
|
|---|
| 348 |
foreach(char[] s; statusList){ |
|---|
| 349 |
status = s; |
|---|
| 350 |
list(testRoot ~ std.path.sep ~ s); |
|---|
| 351 |
} |
|---|
| 352 |
|
|---|
| 353 |
return updateList; |
|---|
| 354 |
} |
|---|
| 355 |
|
|---|
| 356 |
void dropBogusResults(FStime recordTime, char[] testRoot){ |
|---|
| 357 |
uint totalCount = torture.length; |
|---|
| 358 |
|
|---|
| 359 |
char[][] sourcesTorture = torture.keys; |
|---|
| 360 |
foreach(char[] source; sourcesTorture){ |
|---|
| 361 |
if(find(source, "complex/") < 0){ |
|---|
| 362 |
try{ |
|---|
| 363 |
FStime caseTime = getFStime(testRoot~std.path.sep~source); |
|---|
| 364 |
if(caseTime > recordTime){ |
|---|
| 365 |
debug(drop) fwritefln(stderr, "dropped: %s", source); |
|---|
| 366 |
torture.remove(source); |
|---|
| 367 |
} |
|---|
| 368 |
}catch(Exception e){ |
|---|
| 369 |
debug(drop) fwritefln(stderr, "dropped: %s", source); |
|---|
| 370 |
torture.remove(source); |
|---|
| 371 |
} |
|---|
| 372 |
} |
|---|
| 373 |
// asm-filter |
|---|
| 374 |
int i = find(source, "asm_p"); |
|---|
| 375 |
if(i >= 0){ |
|---|
| 376 |
torture.remove(source); |
|---|
| 377 |
} |
|---|
| 378 |
} |
|---|
| 379 |
torture.rehash; |
|---|
| 380 |
|
|---|
| 381 |
writefln("dropped %s outdated tests (%s remaining)", totalCount - torture.length, torture.length); |
|---|
| 382 |
} |
|---|
| 383 |
|
|---|
| 384 |
|
|---|
| 385 |
bool add(char[] line){ |
|---|
| 386 |
const char[] SUB = "Torture-Sub-"; |
|---|
| 387 |
const char[] TORTURE = "Torture:"; |
|---|
| 388 |
|
|---|
| 389 |
line = strip(line); |
|---|
| 390 |
int id = -1; |
|---|
| 391 |
Result r = Result.UNTESTED; |
|---|
| 392 |
|
|---|
| 393 |
if(line.length > SUB.length && line[0 .. SUB.length] == SUB){ |
|---|
| 394 |
line = line[SUB.length .. $]; |
|---|
| 395 |
id = 0; |
|---|
| 396 |
while(line[id] >= '0' && line[id] <= '9'){ |
|---|
| 397 |
id++; |
|---|
| 398 |
} |
|---|
| 399 |
int start = id; |
|---|
| 400 |
id = std.conv.toUint(line[0 .. id]); |
|---|
| 401 |
|
|---|
| 402 |
while(line[start] != '-'){ |
|---|
| 403 |
start++; |
|---|
| 404 |
} |
|---|
| 405 |
line = line[start+1 .. $]; |
|---|
| 406 |
} |
|---|
| 407 |
|
|---|
| 408 |
char[][] token = split(line); |
|---|
| 409 |
if(token.length < 2){ |
|---|
| 410 |
return false; |
|---|
| 411 |
} |
|---|
| 412 |
char[] file = strip(token[1]); |
|---|
| 413 |
|
|---|
| 414 |
switch(token[0]){ |
|---|
| 415 |
case "PASS:": |
|---|
| 416 |
r = Result.PASS; break; |
|---|
| 417 |
case "FAIL:": |
|---|
| 418 |
r = Result.FAIL; break; |
|---|
| 419 |
case "XPASS:": |
|---|
| 420 |
r = Result.XPASS; break; |
|---|
| 421 |
case "XFAIL:": |
|---|
| 422 |
r = Result.XFAIL; break; |
|---|
| 423 |
case "ERROR:": |
|---|
| 424 |
r = Result.ERROR; break; |
|---|
| 425 |
default:{ |
|---|
| 426 |
if(token[0] == TORTURE){ |
|---|
| 427 |
throw new Exception("not yet handled: "~line); |
|---|
| 428 |
}else if(id > -1){ |
|---|
| 429 |
throw new Exception(format("bug in SUB line: (%s) %s", id, line)); |
|---|
| 430 |
} |
|---|
| 431 |
} |
|---|
| 432 |
} |
|---|
| 433 |
|
|---|
| 434 |
if(r != Result.UNTESTED){ |
|---|
| 435 |
if(std.string.find(line, "bad error message") > -1){ |
|---|
| 436 |
r |= Result.BAD_MSG; |
|---|
| 437 |
} |
|---|
| 438 |
if(std.string.find(line, "bad debugger message") > -1){ |
|---|
| 439 |
r |= Result.BAD_MSG; |
|---|
| 440 |
} |
|---|
| 441 |
|
|---|
| 442 |
file = cleanFileName(file); |
|---|
| 443 |
|
|---|
| 444 |
if(id >= 0){ |
|---|
| 445 |
// update sub |
|---|
| 446 |
id--; |
|---|
| 447 |
|
|---|
| 448 |
TortureTest* test = file in torture; |
|---|
| 449 |
|
|---|
| 450 |
if(test is null){ |
|---|
| 451 |
TortureTest t = new TortureTest(file); |
|---|
| 452 |
torture[file] = t; |
|---|
| 453 |
t.r[id] = r; |
|---|
| 454 |
}else{ |
|---|
| 455 |
if(test.r[id] != Result.UNTESTED){ |
|---|
| 456 |
test.r[] = Result.UNTESTED; |
|---|
| 457 |
} |
|---|
| 458 |
test.r[id] = r; |
|---|
| 459 |
} |
|---|
| 460 |
} |
|---|
| 461 |
return true; |
|---|
| 462 |
} |
|---|
| 463 |
return false; |
|---|
| 464 |
} |
|---|
| 465 |
} |
|---|
| 466 |
|
|---|
| 467 |
class Regression{ |
|---|
| 468 |
Result before; |
|---|
| 469 |
Result after; |
|---|
| 470 |
char[] file; |
|---|
| 471 |
char[] name; |
|---|
| 472 |
char[] extInfo; |
|---|
| 473 |
|
|---|
| 474 |
this(char[] name, char[] file, Result oldR, Result newR, char[] addonInfo=null){ |
|---|
| 475 |
this.file = file; |
|---|
| 476 |
this.name = name; |
|---|
| 477 |
before = oldR; |
|---|
| 478 |
after = newR; |
|---|
| 479 |
extInfo = addonInfo; |
|---|
| 480 |
} |
|---|
| 481 |
|
|---|
| 482 |
char[] toString(){ |
|---|
| 483 |
char[] back = .toString(before) ~" -> "~.toString(after)~": "~file; |
|---|
| 484 |
if(extInfo.length > 0){ |
|---|
| 485 |
back ~= " ("~extInfo~")"; |
|---|
| 486 |
} |
|---|
| 487 |
return back; |
|---|
| 488 |
} |
|---|
| 489 |
} |
|---|
| 490 |
|
|---|
| 491 |
class Report{ |
|---|
| 492 |
char[] root; |
|---|
| 493 |
Log[] log; |
|---|
| 494 |
static const char[] header = |
|---|
| 495 |
"<th> </th><th>-g</th><th>-inline</th>" |
|---|
| 496 |
"<th>-fPIC</th><th>-O</th><th>-release</th>" |
|---|
| 497 |
"<th>-g -inline</th><th>-g -fPIC</th><th>-g -O</th>" |
|---|
| 498 |
"<th>-g -release</th><th>-inline -fPIC</th>" |
|---|
| 499 |
"<th>-inline -O</th><th>-inline -release</th>" |
|---|
| 500 |
"<th>-fPIC -O</th><th>-fPIC -release</th>" |
|---|
| 501 |
"<th>-O -release</th><th>-g -inline -fPIC</th>" |
|---|
| 502 |
"<th>-g -inline -O</th><th>-g -inline -release</th>" |
|---|
| 503 |
"<th>-g -fPIC -O</th><th>-g -fPIC -release</th>" |
|---|
| 504 |
"<th>-g -O -release</th><th>-inline -fPIC -O</th>" |
|---|
| 505 |
"<th>-inline -fPIC -release</th><th>-inline -O -release</th>" |
|---|
| 506 |
"<th>-fPIC -O -release</th><th>-g -inline -fPIC -O</th>" |
|---|
| 507 |
"<th>-g -inline -fPIC -release</th>" |
|---|
| 508 |
"<th>-g -fPIC -O -release</th>" |
|---|
| 509 |
"<th>-inline -fPIC -O -release</th>" |
|---|
| 510 |
"<th>-g -inline -fPIC -O -release</th>" |
|---|
| 511 |
"<th>-g -inline -O -release</th>"; |
|---|
| 512 |
|
|---|
| 513 |
this(char[] root, Log[] log){ |
|---|
| 514 |
this.root = root; |
|---|
| 515 |
this.log = log; |
|---|
| 516 |
} |
|---|
| 517 |
|
|---|
| 518 |
void toHtml(OutputStream summary, OutputStream[] cases, bool[] hotspot){ |
|---|
| 519 |
|
|---|
| 520 |
if(cases.length != log.length || cases.length != hotspot.length){ |
|---|
| 521 |
throw new Exception("unexpected argument length"); |
|---|
| 522 |
} |
|---|
| 523 |
foreach(size_t i, Log l; log){ |
|---|
| 524 |
toHtml(l, cases[i]); |
|---|
| 525 |
} |
|---|
| 526 |
toHtmlSummary(summary, hotspot); |
|---|
| 527 |
} |
|---|
| 528 |
|
|---|
| 529 |
static char[] cleanFileName(char[] name){ |
|---|
| 530 |
int i = rfind(name, "_"); |
|---|
| 531 |
if(i > -1){ |
|---|
| 532 |
name = name[i+1 .. $]; |
|---|
| 533 |
} |
|---|
| 534 |
i = rfind(name, "."); |
|---|
| 535 |
if(i > -1){ |
|---|
| 536 |
name = name[0 .. i]; |
|---|
| 537 |
} |
|---|
| 538 |
return name; |
|---|
| 539 |
} |
|---|
| 540 |
|
|---|
| 541 |
static char[] streamLine(uint[] stats){ |
|---|
| 542 |
char[] buffer; |
|---|
| 543 |
foreach(uint i; stats){ |
|---|
| 544 |
buffer ~= "<td>"~std.string.toString(i)~"</td>"; |
|---|
| 545 |
} |
|---|
| 546 |
return buffer; |
|---|
| 547 |
} |
|---|
| 548 |
|
|---|
| 549 |
static void toHtml(Log log, OutputStream stream){ |
|---|
| 550 |
char[] cleanName = cleanFileName(log.id); |
|---|
| 551 |
{ // header |
|---|
| 552 |
char[] name = toupper(cleanName); |
|---|
| 553 |
|
|---|
| 554 |
stream.writeLine("<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>"); |
|---|
| 555 |
stream.writeLine("<html xmlns='http://www.w3.org/1999/xhtml' lang='en' xml:lang='en'>"); |
|---|
| 556 |
|
|---|
| 557 |
stream.writeLine("<head><title>DStress - Torture: "~name~"</title><link rel='stylesheet' type='text/css' href='formate.css' /><meta name='author' content='Thomas Kühne' /><meta name='date' content='" ~ dateString() ~ "' /><link rel='shortcut icon' href='data:image/gif;base64,R0lGODlhEAAQAKEAALMfEuWoLv///7MfEiH5BAEKAAMALAAAAAAQABAAAAIpnI95wN06nARqyvSQM6Gz+g3dOGofiabqygYmNYbv/FKLld04aEG+UgAAOw==' type='image/gif' /></head>"); |
|---|
| 558 |
stream.writeLine("<body><center><h1>DStress - Torture: "~name~"</h1></center><center><small>by Thomas Kühne <<a href='mailto:thomas@kuehne.cn'>thomas@kuehne.cn</a>></small></center>"); |
|---|
| 559 |
stream.writeLine("<h2><a name='note' id='note'></a>Note</h2><blockquote>A detailed description of the testing and the used symbols can be found on the <a href='./dstress.html'>main page</a>.</blockquote>"); |
|---|
| 560 |
} |
|---|
| 561 |
|
|---|
| 562 |
{ // stats |
|---|
| 563 |
stream.writeLine("<h2><a name='summary' id='summary'></a>Summary</h2>"); |
|---|
| 564 |
uint[][] stats; |
|---|
| 565 |
stats.length = 6; |
|---|
| 566 |
foreach(inout uint[] array; stats){ |
|---|
| 567 |
array.length = TORTURE_FLAGS.length; |
|---|
| 568 |
} |
|---|
| 569 |
|
|---|
| 570 |
foreach(TortureTest t; log.torture){ |
|---|
| 571 |
foreach(int i, Result r; t.r){ |
|---|
| 572 |
stats[r >> 2][i]++; |
|---|
| 573 |
} |
|---|
| 574 |
} |
|---|
| 575 |
|
|---|
| 576 |
|
|---|
| 577 |
{ // total |
|---|
| 578 |
uint total = 0; |
|---|
| 579 |
|
|---|
| 580 |
foreach(uint[] cases; stats){ |
|---|
| 581 |
total += cases[0]; |
|---|
| 582 |
} |
|---|
| 583 |
|
|---|
| 584 |
uint config = 0; |
|---|
| 585 |
foreach(uint[] a; stats[1 .. $]){ |
|---|
| 586 |
foreach(uint b; a){ |
|---|
| 587 |
config += b; |
|---|
| 588 |
} |
|---|
| 589 |
} |
|---|
| 590 |
|
|---|
| 591 |
stream.writeLine(format("<blockquote><dl><dt><strong>test cases:</strong></dt><dd>%d</dd><dt><strong>tested configurations:</strong></dt><dd>%d</dd></dl></blockquote>", total, config)); |
|---|
| 592 |
} |
|---|
| 593 |
|
|---|
| 594 |
|
|---|
| 595 |
stream.writeLine("<table border='1' summary='nummeric summary of the test results'>"); |
|---|
| 596 |
stream.writeLine("\t<tr><td> </td>"~header~"</tr>"); |
|---|
| 597 |
stream.writeLine("\t<tr class='" ~ cast(char)('A'+Result.PASS)~"'><th>PASS</th>" ~ streamLine(stats[Result.PASS >> 2])~"</tr>"); |
|---|
| 598 |
stream.writeLine("\t<tr class='" ~ cast(char)('A'+Result.XFAIL)~"'><th>XFAIL</th>" ~ streamLine(stats[Result.XFAIL >> 2])~"</tr>"); |
|---|
| 599 |
stream.writeLine("\t<tr class='" ~ cast(char)('A'+Result.XPASS)~"'><th>XPASS</th>" ~ streamLine(stats[Result.XPASS >> 2])~"</tr>"); |
|---|
| 600 |
stream.writeLine("\t<tr class='" ~ cast(char)('A'+Result.FAIL)~"'><th>FAIL</th>" ~ streamLine(stats[Result.FAIL >> 2])~"</tr>"); |
|---|
| 601 |
stream.writeLine("\t<tr class='" ~ cast(char)('A'+Result.ERROR)~"'><th>ERROR</th>" ~ streamLine(stats[Result.ERROR >> 2])~"</tr>"); |
|---|
| 602 |
stream.writeLine("\t<tr class='" ~ cast(char)('A'+Result.UNTESTED)~"'><th>untested</th>" ~ streamLine(stats[Result.UNTESTED >> 2])~"</tr>"); |
|---|
| 603 |
stream.writeLine("</table>"); |
|---|
| 604 |
} |
|---|
| 605 |
|
|---|
| 606 |
{ // details |
|---|
| 607 |
stream.writeLine("<h2><a name='details' id='details'></a>Details</h2>"); |
|---|
| 608 |
stream.writeLine("<table border='1' summary='detailed listing of all test cases with unexpected results'>"); |
|---|
| 609 |
stream.writeLine("<tr><td> </td>"~header~"</tr>"); |
|---|
| 610 |
|
|---|
| 611 |
char[][] keys; |
|---|
| 612 |
{ |
|---|
| 613 |
char[][char[]] k; |
|---|
| 614 |
foreach(char[] org; log.torture.keys){ |
|---|
| 615 |
char[] z = org; |
|---|
| 616 |
int i = rfind(z, "/"); |
|---|
| 617 |
if(i > -1){ |
|---|
| 618 |
z = z[i+1 .. $]; |
|---|
| 619 |
} |
|---|
| 620 |
i = rfind(z, "."); |
|---|
| 621 |
if(i > -1){ |
|---|
| 622 |
z = z[0 .. i]; |
|---|
| 623 |
} |
|---|
| 624 |
if(z in k && find(org, "complex") < 0){ |
|---|
| 625 |
throw new Exception("dublicate key "~org); |
|---|
| 626 |
} |
|---|
| 627 |
k[z] = org; |
|---|
| 628 |
} |
|---|
| 629 |
|
|---|
| 630 |
foreach(char[] x; k.keys.sort){ |
|---|
| 631 |
keys ~= k[x]; |
|---|
| 632 |
} |
|---|
| 633 |
|
|---|
| 634 |
} |
|---|
| 635 |
foreach(char[] key; keys){ |
|---|
| 636 |
TortureTest t = log.torture[key]; |
|---|
| 637 |
Result plainR = t.condensed(); |
|---|
| 638 |
|
|---|
| 639 |
if(plainR == Result.PASS |
|---|
| 640 |
|| plainR == Result.XFAIL |
|---|
| 641 |
|| plainR == Result.UNTESTED) |
|---|
| 642 |
{ |
|---|
| 643 |
continue; |
|---|
| 644 |
}else{ |
|---|
| 645 |
char[] name = replace(t.name, "_", " "); |
|---|
| 646 |
char[] src = t.file; |
|---|
| 647 |
if(find(src,"complex/") != -1){ |
|---|
| 648 |
src = src[0 .. rfind(src, "/")]; |
|---|
| 649 |
} |
|---|
| 650 |
char[] back = "<tr><th><a href=\"../"~src~"\" id='"~t.name~"'>"~name~"</a></th>"; |
|---|
| 651 |
foreach(Result r; t.r){ |
|---|
| 652 |
back ~= "<td class='" ~ cast(char)(r+'A') ~ "'>"; |
|---|
| 653 |
if(r == Result.UNTESTED){ |
|---|
| 654 |
back ~= "-"; |
|---|
| 655 |
}else{ |
|---|
| 656 |
try{ |
|---|
| 657 |
back ~= .toString(r & Result.BASE_MASK); |
|---|
| 658 |
}catch(Exception e){ |
|---|
| 659 |
throw new Exception(t.toString()~" ["~e.toString()~"]"); |
|---|
| 660 |
} |
|---|
| 661 |
} |
|---|
| 662 |
back ~= "</td>"; |
|---|
| 663 |
} |
|---|
| 664 |
stream.writeLine(back ~ "</tr>"); |
|---|
| 665 |
} |
|---|
| 666 |
} |
|---|
| 667 |
|
|---|
| 668 |
stream.writeLine("<tr><td> </td>"~header~"</tr>"); |
|---|
| 669 |
stream.writeLine("</table>"); |
|---|
| 670 |
} |
|---|
| 671 |
|
|---|
| 672 |
{ // footer |
|---|
| 673 |
stream.writeLine("<div><br /><br /><hr /><a href='http://dstress.kuehne.cn/www/"~cleanName~".html'>http://dstress.kuehne.cn/www/"~cleanName~".html</a>   " ~ dateString() ~ "</div>"); |
|---|
| 674 |
stream.writeLine("<center><a href='http://developer.berlios.de'><img src='http://developer.berlios.de/bslogo.php?group_id=2732' width='124' height='32' border='0' alt='BerliOS Logo' /></a></center>"); |
|---|
| 675 |
|
|---|
| 676 |
stream.writeLine("</body></html>"); |
|---|
| 677 |
} |
|---|
| 678 |
} |
|---|
| 679 |
|
|---|
| 680 |
void toHtmlSummary(OutputStream stream, bool[] hotspot){ |
|---|
| 681 |
if(hotspot.length != log.length){ |
|---|
| 682 |
throw new Exception("illegal hotspot length"); |
|---|
| 683 |
} |
|---|
| 684 |
|
|---|
| 685 |
char[][] names; |
|---|
| 686 |
|
|---|
| 687 |
foreach(Log l; log){ |
|---|
| 688 |
names ~= l.torture.keys; |
|---|
| 689 |
} |
|---|
| 690 |
|
|---|
| 691 |
uint[][] stats; |
|---|
| 692 |
stats.length = 6; |
|---|
| 693 |
foreach(inout uint[] array; stats){ |
|---|
| 694 |
array.length = log.length; |
|---|
| 695 |
} |
|---|
| 696 |
|
|---|
| 697 |
|
|---|
| 698 |
char[][char[]] keys; |
|---|
| 699 |
{ |
|---|
| 700 |
foreach(char[] org; unique(names)){ |
|---|
| 701 |
char[] z = org; |
|---|
| 702 |
int i = rfind(z, "/"); |
|---|
| 703 |
if(i > -1){ |
|---|
| 704 |
z = z[i+1 .. $]; |
|---|
| 705 |
} |
|---|
| 706 |
i = rfind(z, "."); |
|---|
| 707 |
if(i > -1){ |
|---|
| 708 |
z = z[0 .. i]; |
|---|
| 709 |
} |
|---|
| 710 |
if(z in keys && -1 == find(org, "complex/")){ |
|---|
| 711 |
throw new Exception("dublicate key "~org); |
|---|
| 712 |
} |
|---|
| 713 |
keys[z] = org; |
|---|
| 714 |
} |
|---|
| 715 |
|
|---|
| 716 |
} |
|---|
| 717 |
|
|---|
| 718 |
{ // total |
|---|
| 719 |
uint total = keys.keys.length; |
|---|
| 720 |
for(size_t i = 0; i < stats[0].length; i++){ |
|---|
| 721 |
stats[0][i] = total; |
|---|
| 722 |
} |
|---|
| 723 |
} |
|---|
| 724 |
|
|---|
| 725 |
char[][] badLines; |
|---|
| 726 |
foreach(char[] name; keys.keys.sort){ |
|---|
| 727 |
char[] file = keys[name]; |
|---|
| 728 |
Result[] result = new Result[log.length]; |
|---|
| 729 |
bool isBadLine; |
|---|
| 730 |
foreach(size_t i, Log l; log){ |
|---|
| 731 |
auto t = file in l.torture; |
|---|
| 732 |
|
|---|
| 733 |
if(t){ |
|---|
| 734 |
Result r = t.condensed(); |
|---|
| 735 |
result[i] = r; |
|---|
| 736 |
|
|---|
| 737 |
if(r != Result.UNTESTED){ |
|---|
| 738 |
stats[result[i] >> 2][i]++; |
|---|
| 739 |
stats[0][i]--; |
|---|
| 740 |
if(hotspot[i] && r>= Result.XPASS){ |
|---|
| 741 |
isBadLine = true; |
|---|
| 742 |
} |
|---|
| 743 |
} |
|---|
| 744 |
} |
|---|
| 745 |
|
|---|
| 746 |
} |
|---|
| 747 |
|
|---|
| 748 |
if(isBadLine){ |
|---|
| 749 |
char[] cleanName = replace(name, "_", " "); |
|---|
| 750 |
char[] back = "<tr><th><a href=\"../"~file~"\" id='"~name~"'>"~cleanName~"</a></th>"; |
|---|
| 751 |
foreach(Result r; result){ |
|---|
| 752 |
back ~= "<td class='" ~ cast(char)(r+'A') ~ "'>"; |
|---|
| 753 |
if(r == Result.UNTESTED){ |
|---|
| 754 |
back ~= "-"; |
|---|
| 755 |
}else{ |
|---|
| 756 |
try{ |
|---|
| 757 |
back ~= .toString(r & Result.BASE_MASK); |
|---|
| 758 |
}catch(Exception e){ |
|---|
| 759 |
throw new Exception("name:" ~name~" ["~e.toString()~"]"); |
|---|
| 760 |
} |
|---|
| 761 |
} |
|---|
| 762 |
back ~= "</td>"; |
|---|
| 763 |
} |
|---|
| 764 |
badLines ~= back ~ "</tr>"; |
|---|
| 765 |
} |
|---|
| 766 |
} |
|---|
| 767 |
|
|---|
| 768 |
|
|---|
| 769 |
// output |
|---|
| 770 |
stream.writeLine("<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>"); |
|---|
| 771 |
stream.writeLine("<html xmlns='http://www.w3.org/1999/xhtml'>"); |
|---|
| 772 |
stream.writeLine("<head><title>DStress Report</title><link rel='stylesheet' type='text/css' href='formate.css' /><meta name='author' content='Thomas Kühne' /><meta name='date' content='"~dateString()~"' /></head>"); |
|---|
| 773 |
stream.writeLine("<body><center><h1>DStress Report</h1></center>"); |
|---|
| 774 |
stream.writeLine("<h2><a name='note' id='note'></a>Note</h2>"); |
|---|
| 775 |
stream.writeLine("<blockquote><p>A detailed description of the testing and the used symbols can be found on the <a href='./dstress.html'>main page</a>.</p></blockquote>"); |
|---|
| 776 |
stream.writeLine("<h2><a name='summary' id='summary'></a>Summary</h2>"); |
|---|
| 777 |
|
|---|
| 778 |
stream.writeLine("<table border='1' summary='nummeric summary of the test results'>"); |
|---|
| 779 |
char[] versionHeader = "<tr><td> </td>"; |
|---|
| 780 |
{ // version header |
|---|
| 781 |
foreach(Log l; log){ |
|---|
| 782 |
char[] name = l.id; |
|---|
| 783 |
int i = rfind(name, "/"); |
|---|
| 784 |
if(i > -1){ |
|---|
| 785 |
name = name[i+1 .. $]; |
|---|
| 786 |
} |
|---|
| 787 |
i = rfind(name, ".log"); |
|---|
| 788 |
if(i + ".log".length == name.length){ |
|---|
| 789 |
name = name[0 .. i]; |
|---|
| 790 |
} |
|---|
| 791 |
versionHeader ~= "<th><a href='./" ~ cleanFileName(l.id) ~ ".html'>"~replace(name, "_", " ")~"</a></th>"; |
|---|
| 792 |
} |
|---|
| 793 |
versionHeader ~= "</tr>"; |
|---|
| 794 |
} |
|---|
| 795 |
|
|---|
| 796 |
stream.writeLine("\t<tr><td> </td>" |
|---|
| 797 |
"<th class='" ~ cast(char)('A'+Result.PASS)~"'>PASS</th>" |
|---|
| 798 |
"<th class='" ~ cast(char)('A'+Result.XFAIL)~"'>XFAIL</th>" |
|---|
| 799 |
"<th class='" ~ cast(char)('A'+Result.XPASS)~"'>XPASS</th>" |
|---|
| 800 |
"<th class='" ~ cast(char)('A'+Result.FAIL)~"'>FAIL</th>" |
|---|
| 801 |
"<th class='" ~ cast(char)('A'+Result.ERROR)~"'>ERROR</th>" |
|---|
| 802 |
"<th class='" ~ cast(char)('A'+Result.UNTESTED)~"'>UNTESTED</th></tr>"); |
|---|
| 803 |
foreach(size_t j, Log l; log){ |
|---|
| 804 |
char[] row ="\t<tr>"; |
|---|
| 805 |
char[] name = l.id; |
|---|
| 806 |
int i = rfind(name, "/"); |
|---|
| 807 |
if(i > -1){ |
|---|
| 808 |
name = name[i+1 .. $]; |
|---|
| 809 |
} |
|---|
| 810 |
i = rfind(name, ".log"); |
|---|
| 811 |
if(i + ".log".length == name.length){ |
|---|
| 812 |
name = name[0 .. i]; |
|---|
| 813 |
} |
|---|
| 814 |
row ~= "<th><a href='./" ~ cleanFileName(l.id) ~ ".html'>"~replace(name, "_", " ")~"</a></th>"; |
|---|
| 815 |
row ~= "<td class='" ~ cast(char)('A'+Result.PASS)~"'>" ~ std.string.toString(stats[Result.PASS >> 2][j]) ~"</td>"; |
|---|
| 816 |
row ~= "<td class='" ~ cast(char)('A'+Result.XFAIL)~"'>" ~ std.string.toString(stats[Result.XFAIL >> 2][j]) ~"</td>"; |
|---|
| 817 |
row ~= "<td class='" ~ cast(char)('A'+Result.XPASS)~"'>" ~ std.string.toString(stats[Result.XPASS >> 2][j]) ~"</td>"; |
|---|
| 818 |
row ~= "<td class='" ~ cast(char)('A'+Result.FAIL)~"'>" ~ std.string.toString(stats[Result.FAIL >> 2][j]) ~"</td>"; |
|---|
| 819 |
row ~= "<td class='" ~ cast(char)('A'+Result.ERROR)~"'>" ~ std.string.toString(stats[Result.ERROR >> 2][j]) ~"</td>"; |
|---|
| 820 |
row ~= "<td class='" ~ cast(char)('A'+Result.UNTESTED)~"'>" ~ std.string.toString(stats[Result.UNTESTED >> 2][j]) ~"</td>"; |
|---|
| 821 |
stream.writeLine(row ~ "</tr>"); |
|---|
| 822 |
} |
|---|
| 823 |
stream.writeLine("</table>"); |
|---|
| 824 |
|
|---|
| 825 |
stream.writeLine("<h2><a name='details' id='details'></a>Details</h2>"); |
|---|
| 826 |
stream.writeLine("<table border='1'>"); |
|---|
| 827 |
stream.writeLine(versionHeader); |
|---|
| 828 |
foreach(char[] line; badLines){ |
|---|
| 829 |
stream.writeLine(line); |
|---|
| 830 |
} |
|---|
| 831 |
stream.writeLine(versionHeader); |
|---|
| 832 |
stream.writeLine("</table>"); |
|---|
| 833 |
|
|---|
| 834 |
stream.writeLine("<div><br /><br /><hr /><a href='http://dstress.kuehne.cn/www/results.html'>http://dstress.kuehne.cn/www/results.html</a>   "~dateString()~"</div>"); |
|---|
| 835 |
|
|---|
| 836 |
stream.writeLine("<!-- Start of StatCounter Code -->"); |
|---|
| 837 |
stream.writeLine("<script type='text/javascript'><!-- var sc_project=1337754; var sc_invisible=1; var sc_partition=12; var sc_security=\"a4a998fe\"; var sc_remove_link=1; //--> </script>"); |
|---|
| 838 |
stream.writeLine("<script type='text/javascript' src='http://www.statcounter.com/counter/counter_xhtml.js'></script><noscript><div class='statcounter'><img src='http://c13.statcounter.com/counter.php?sc_project=1337754&amp;java=0&amp;security=a4a998fe&amp;invisible=1' class='statcounter' alt='counter' /></div></noscript>"); |
|---|
| 839 |
stream.writeLine("<!-- End of StatCounter Code -->"); |
|---|
| 840 |
|
|---|
| 841 |
stream.writeLine("</body></html>"); |
|---|
| 842 |
} |
|---|
| 843 |
} |
|---|
| 844 |
|
|---|
| 845 |
int main(char[][] args){ |
|---|
| 846 |
|
|---|
| 847 |
if(args.length < 4){ |
|---|
| 848 |
fwritefln(stderr, "%s <command> <root> <log.1> [<log.2> ...]", args[0]); |
|---|
| 849 |
fwritefln(stderr, "known commands: genUpdateList findRegressions genReport"); |
|---|
| 850 |
return 1; |
|---|
| 851 |
} |
|---|
| 852 |
|
|---|
| 853 |
|
|---|
| 854 |
char[] command = args[1]; |
|---|
| 855 |
char[] root = args[2]; |
|---|
| 856 |
if(root.length < 1){ |
|---|
| 857 |
root = "."; |
|---|
| 858 |
} |
|---|
| 859 |
debug fwritefln(stderr, "command: %s", command); |
|---|
| 860 |
debug fwritefln(stderr, "root: %s", root); |
|---|
| 861 |
|
|---|
| 862 |
Report report = new Report(root, null); |
|---|
| 863 |
bool[] hotspot; |
|---|
| 864 |
|
|---|
| 865 |
switch(command){ |
|---|
| 866 |
case "genUpdateList", "findRegressions", "genReport": break; |
|---|
| 867 |
default:{ |
|---|
| 868 |
fwritefln(stderr, "unknown command: %s", command); |
|---|
| 869 |
return -1; |
|---|
| 870 |
} |
|---|
| 871 |
} |
|---|
| 872 |
|
|---|
| 873 |
foreach(size_t id, char[] file; args[3 .. $]){ |
|---|
| 874 |
if(file.length > "--".length && file[0 .. 2] == "--"){ |
|---|
| 875 |
file = file[2..$]; |
|---|
| 876 |
hotspot ~= true; |
|---|
| 877 |
}else{ |
|---|
| 878 |
hotspot ~= false; |
|---|
| 879 |
} |
|---|
| 880 |
|
|---|
| 881 |
writefln("parsing: %s", file); |
|---|
| 882 |
FStime logTime = getFStime(file); |
|---|
| 883 |
debug fwritefln(stderr, "sourceTime: %s", logTime); |
|---|
| 884 |
|
|---|
| 885 |
Log l= new Log(file); |
|---|
| 886 |
Stream source = new BufferedFile(file, FileMode.In); |
|---|
| 887 |
while(!source.eof()){ |
|---|
| 888 |
l.add(source.readLine()); |
|---|
| 889 |
} |
|---|
| 890 |
|
|---|
| 891 |
l.dropBogusResults(logTime, root); |
|---|
| 892 |
|
|---|
| 893 |
report.log ~= l; |
|---|
| 894 |
} |
|---|
| 895 |
|
|---|
| 896 |
switch(command){ |
|---|
| 897 |
case "genUpdateList":{ |
|---|
| 898 |
foreach(Log l; report.log){ |
|---|
| 899 |
char[][] update = l.genUpdateList(root); |
|---|
| 900 |
writefln("%s updates required (%s still up to date)", update.length, l.torture.length); |
|---|
| 901 |
|
|---|
| 902 |
char[] updateFile = l.id ~ ".update"; |
|---|
| 903 |
try{std.file.remove(updateFile);}catch{} |
|---|
| 904 |
|
|---|
| 905 |
if(update.length){ |
|---|
| 906 |
File f = new File(updateFile, FileMode.OutNew); |
|---|
| 907 |
foreach(char[] line; update){ |
|---|
| 908 |
f.writeLine(line); |
|---|
| 909 |
} |
|---|
| 910 |
f.close(); |
|---|
| 911 |
} |
|---|
| 912 |
} |
|---|
| 913 |
break; |
|---|
| 914 |
}case "findRegressions":{ |
|---|
| 915 |
foreach(size_t i, Log newLog; report.log[1..$]){ |
|---|
| 916 |
Regression[] newReg = newLog.findRegressions(report.log[i]); |
|---|
| 917 |
writefln("identified %s new regressions for %s", newReg.length, newLog.id); |
|---|
| 918 |
|
|---|
| 919 |
Regression[] oldReg; |
|---|
| 920 |
{ |
|---|
| 921 |
Regression[] oldRegT = newLog.findGlobalRegressions(report.log[0 .. i]); |
|---|
| 922 |
foreach(Regression a; oldRegT){ |
|---|
| 923 |
foreach(Regression b; newReg){ |
|---|
| 924 |
if(a.file == b.file && (!a.extInfo || a.extInfo == b.extInfo)){ |
|---|
| 925 |
goto handled; |
|---|
| 926 |
} |
|---|
| 927 |
} |
|---|
| 928 |
|
|---|
| 929 |
oldReg ~= a; |
|---|
| 930 |
handled: {} |
|---|
| 931 |
} |
|---|
| 932 |
} |
|---|
| 933 |
|
|---|
| 934 |
writefln("identified %s old regressions for %s", oldReg.length, newLog.id); |
|---|
| 935 |
|
|---|
| 936 |
char[] regressionFile = newLog.id ~ ".regression"; |
|---|
| 937 |
try{std.file.remove(regressionFile);}catch{} |
|---|
| 938 |
|
|---|
| 939 |
if(newReg.length + oldReg.length){ |
|---|
| 940 |
File f = new File(regressionFile, FileMode.OutNew); |
|---|
| 941 |
|
|---|
| 942 |
if(newReg.length){ |
|---|
| 943 |
f.writeLine(format("%s new regressions", newReg.length)); |
|---|
| 944 |
foreach(Regression r; newReg){ |
|---|
| 945 |
f.writeLine(r.toString()); |
|---|
| 946 |
} |
|---|
| 947 |
} |
|---|
| 948 |
|
|---|
| 949 |
if(oldReg.length){ |
|---|
| 950 |
if(newReg.length){ |
|---|
| 951 |
f.writeLine(""); |
|---|
| 952 |
} |
|---|
| 953 |
f.writeLine(format("%s old regressions", oldReg.length)); |
|---|
| 954 |
foreach(Regression r; oldReg){ |
|---|
| 955 |
f.writeLine(r.toString()); |
|---|
| 956 |
} |
|---|
| 957 |
} |
|---|
| 958 |
|
|---|
| 959 |
f.close(); |
|---|
| 960 |
} |
|---|
| 961 |
} |
|---|
| 962 |
break; |
|---|
| 963 |
}case "genReport":{ |
|---|
| 964 |
OutputStream[] html; |
|---|
| 965 |
OutputStream o = new File("./www/results.html", FileMode.OutNew); |
|---|
| 966 |
foreach(Log l; report.log){ |
|---|
| 967 |
html ~= new File("./www/"~Report.cleanFileName(l.id)~".html", FileMode.OutNew); |
|---|
| 968 |
} |
|---|
| 969 |
|
|---|
| 970 |
report.toHtml(o, html, hotspot); |
|---|
| 971 |
|
|---|
| 972 |
o.close(); |
|---|
| 973 |
foreach(OutputStream stream; html){ |
|---|
| 974 |
stream.close(); |
|---|
| 975 |
} |
|---|
| 976 |
|
|---|
| 977 |
break; |
|---|
| 978 |
}default:{ |
|---|
| 979 |
fwritefln(stderr, "unknown command: %s", command); |
|---|
| 980 |
return -1; |
|---|
| 981 |
} |
|---|
| 982 |
} |
|---|
| 983 |
|
|---|
| 984 |
return 0; |
|---|
| 985 |
} |
|---|