26 typedef std::chrono::high_resolution_clock
clockType;
28 clockType::duration t) {
30 stream << std::chrono::duration_cast<std::chrono::duration<double>>(t).count();
39 std::list<std::pair<std::string, std::string>>
parameters;
40 std::list<std::pair<options::base&, std::string>>
options;
58 static std::map<std::string, baseTest*>&
getList() {
59 static std::remove_reference<decltype(
getList())>::type list;
69 const std::string& aDescription,
71 const decltype(
options)& aOptions = {}):
84 for (
const auto& opt :
options) {
104 virtual void execute(
const std::vector<std::string>& remainingOptions) = 0;
107 auto bytesToWrite = data.size();
108 auto ptr = data.data();
109 while (bytesToWrite > 0) {
110 auto result = write(fd, ptr, bytesToWrite);
112 if (errno == EINTR) {
118 bytesToWrite -= result;
123 auto bytesToRead = data.size();
124 auto ptr =
const_cast<char*
>(data.data());
125 while (bytesToRead > 0) {
126 auto result = read(fd, ptr, bytesToRead);
127 if (result == -1 && errno == EINTR) {
131 bytesToRead -= result;
134 void Execute(
const std::vector<std::string>& remainingOptions) {
141 auto fd =
throwcall::badval(open(
"/dev/urandom", O_RDONLY), -1,
"open /dev/urandom");
149 for (
unsigned i = 0;
true; i++) {
150 auto number = std::to_string(i);
166 struct stat statData;
168 content.resize(statData.st_size);
172 std::vector<clockType::duration> times;
175 for (
unsigned i = 0; i <
repeat; i++) {
177 auto now = std::chrono::system_clock::now();
178 auto seconds = std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()).count();
179 auto divresult = std::div(seconds,
syncus);
180 auto then = std::chrono::system_clock::time_point(std::chrono::microseconds(divresult.quot *
syncus +
syncus + (i %
syncus) *
skewus));
181 std::this_thread::sleep_until(then);
183 auto tStart = clockType::now();
185 auto tStop = clockType::now();
186 auto dt = tStop - tStart;
191 std::cout <<
"removing " << file <<
"\n";
197 std::cout <<
"took " << times.at(0) <<
"s\n";
199 auto minMax = std::minmax_element(times.begin(), times.end());
200 std::cout <<
"minimum time: " << *(minMax.first) <<
"s\n";
201 std::cout <<
"maximum time: " << *(minMax.second) <<
"s\n";
202 auto sum = std::accumulate(times.begin(), times.end(), clockType::duration::zero());
203 auto mean = std::chrono::duration_cast<std::chrono::duration<double>>(sum).count() /
repeat;
204 std::cout <<
"average time: " << mean <<
"s\n";
205 std::map<double, unsigned long> histogram;
207 auto range = std::chrono::duration_cast<std::chrono::duration<double>>(*(minMax.second) - * (minMax.first)).count();
208 auto lowBorder = std::chrono::duration_cast<std::chrono::duration<double>>(*(minMax.first)).count();
209 if (mean < lowBorder + range / 10) {
210 auto border = lowBorder;
211 histogram[border] = 0;
212 auto factor = std::pow(std::chrono::duration_cast<std::chrono::duration<double>>(*(minMax.second)).count() / lowBorder,
214 for (
unsigned i = 0; i < bins; i++) {
216 histogram[border] = 0;
219 lowBorder -= range / (2 * bins);
223 for (
unsigned i = 0; i < bins + 1; i++) {
224 histogram[lowBorder + i * range / bins] = 0;
227 for (
auto t : times) {
228 auto bin = histogram.begin();
229 for (
auto testBin = bin; testBin != histogram.end(); ++testBin) {
230 if (std::chrono::duration_cast<std::chrono::duration<double>>(t).count() < testBin->first) {
237 for (
auto& bin : histogram) {
238 std::cout << bin.first <<
": " << bin.second <<
"\n";
245 "repeat this many times", 1);
247 "remove created files after running tests");
249 "get content from /dev/urandom");
251 "set content to decimal ascenfing numbers");
253 "name of file for content",
"");
255 "size of content string", 0);
257 "number of blocks to write", 1);
260 "content of file",
"blabla");
262 "use O_TRUNC on open()");
264 "characteristic test time (s)",
265 std::chrono::seconds(0));
267 "sync start times to sync microseconds", 0);
269 "skew start times by skew microseconds", 0);
274 "create a file via O_TMPFILE", {
275 {
"dir",
"directory to create the file in"},
276 {
"file",
"path to the file to create"}
278 void execute(
const std::vector<std::string>& remainingOptions)
override {
279 auto dir = remainingOptions.at(0);
280 auto file = remainingOptions.at(1);
281 auto result = unlink(file.c_str());
282 if (result == -1 && errno != ENOENT) {
286 "open anonymous file in ", dir);
288 std::string path(
"/proc/self/fd/");
289 path += std::to_string(fd);
290 throwcall::good0(linkat(AT_FDCWD, path.c_str(), AT_FDCWD, file.c_str(), AT_SYMLINK_FOLLOW),
291 "link ", path,
" to ", file);
301 "simulate the way vim writes a file", {
302 {
"file",
"path to the file to create"
306 void execute(
const std::vector<std::string>& remainingOptions)
override {
307 auto file = remainingOptions.at(0);
308 std::string tildeFile(file);
311 unlink(tildeFile.c_str());
312 if (stat(file.c_str(), &buf) != 0) {
313 if (errno == ENOENT) {
314 auto fd =
throwcall::badval(open(file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644), -1,
323 "rename ", file,
" to ", tildeFile);
324 auto fd =
throwcall::badval(open(file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644), -1,
339 "open file for reading and keep open", {
340 {
"file",
"path to file"
343 {{
time,
"keep open this long"}}) {};
344 void execute(
const std::vector<std::string>& remainingOptions)
override {
345 auto file = remainingOptions.at(0);
347 std::this_thread::sleep_for(
time);
357 "create a file and append blocks to it, changing the first one.", {
358 {
"file",
"path to file"
361 {{
time,
"wait this long between writes"}}) {};
362 void execute(
const std::vector<std::string>& remainingOptions)
override {
363 auto file = remainingOptions.at(0);
364 for (
unsigned i = 0; i <
blocks; i++) {
366 std::this_thread::sleep_for(
time);
368 auto fd =
throwcall::badval(open(file.c_str(), O_CREAT | O_WRONLY, 0744), -1,
"open ", file);
384 "create a file and write into it", {
385 {
"file",
"path to file"
388 {{
time,
"wait this long between writes"},
389 {
truncate,
"use O_TRUNC on open()"}}) {};
390 void execute(
const std::vector<std::string>& remainingOptions)
override {
391 auto file = remainingOptions.at(0);
392 auto fd =
throwcall::badval(open(file.c_str(), O_CREAT | O_WRONLY | (
truncate ? O_TRUNC : 0), 0744), -1,
"open ", file);
393 for (
unsigned i = 0; i <
blocks; i++) {
395 std::this_thread::sleep_for(
time);
409 "create a file and write into it like cp does", {
410 {
"file",
"path to file"
413 {{
time,
"wait this long between writes"}}) {};
414 void execute(
const std::vector<std::string>& remainingOptions)
override {
415 auto file = remainingOptions.at(0);
416 auto fd =
throwcall::badval(open(file.c_str(), O_CREAT | O_WRONLY | O_EXCL, 0700), -1,
"open ", file);
417 for (
unsigned i = 0; i <
blocks; i++) {
419 std::this_thread::sleep_for(
time);
434 "create a file with O_DIRECT and write into it", {
435 {
"file",
"path to file"
438 {{
time,
"wait this long between writes"}}) {};
439 void execute(
const std::vector<std::string>& remainingOptions)
override {
440 auto file = remainingOptions.at(0);
443 PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS,
446 "mmap ",
content.size(),
" bytes anonymously");
448 auto fd =
throwcall::badval(open(file.c_str(), O_CREAT | O_WRONLY | O_DIRECT, 0744), -1,
"open ", file);
449 for (
unsigned i = 0; i <
blocks; i++) {
451 std::this_thread::sleep_for(
time);
465 "create a file, mmap() it and write into it", {
466 {
"file",
"path to file"
469 void execute(
const std::vector<std::string>& remainingOptions)
override {
470 auto file = remainingOptions.at(0);
471 auto fd =
throwcall::badval(open(file.c_str(), O_CREAT | O_RDWR, 0744), -1,
"open ", file);
473 throwcall::good0(ftruncate(fd, mapSize),
"truncate to ", mapSize,
" bytes");
475 PROT_WRITE, MAP_SHARED,
478 "mmap ",
content.size(),
" bytes from ", file);
479 for (
unsigned i = 0; i <
blocks; i++) {
492 "create a sparse file, data (hole data){blocks}", {
493 {
"file",
"path to file"
496 void execute(
const std::vector<std::string>& remainingOptions)
override {
497 auto file = remainingOptions.at(0);
498 auto fd =
throwcall::badval(open(file.c_str(), O_CREAT | O_WRONLY, 0744), -1,
"open ", file);
500 for (
unsigned i = 0; i <
blocks; i++) {
502 "lseek in ", file,
" for ",
content.size(),
" bytes");
514 "create a sparse file, hole (data hole){blocks}", {
515 {
"file",
"path to file"
518 void execute(
const std::vector<std::string>& remainingOptions)
override {
519 auto file = remainingOptions.at(0);
520 auto fd =
throwcall::badval(open(file.c_str(), O_CREAT | O_WRONLY, 0744), -1,
"open ", file);
522 "lseek in ", file,
" for ",
content.size(),
" bytes");
523 for (
unsigned i = 0; i <
blocks; i++) {
527 "lseek in ", file,
" for ",
content.size(),
" bytes");
540 "find minimum hole size", {
541 {
"file",
"path to file"
544 void execute(
const std::vector<std::string>& remainingOptions)
override {
545 auto file = remainingOptions.at(0);
546 auto fd =
throwcall::badval(open(file.c_str(), O_CREAT | O_WRONLY, 0744), -1,
"open ", file);
549 auto testSize = statbuf.st_blksize;
550 while (testSize > 0) {
552 fd =
throwcall::badval(open(file.c_str(), O_WRONLY | O_TRUNC, 0744), -1,
"open ", file);
553 throwcall::badval(lseek(fd, testSize, SEEK_SET), -1,
"seek ", testSize,
" in ", file);
558 auto dataPos =
throwcall::badval(lseek(fd, 0, SEEK_DATA), -1,
"seek data in ", file);
559 if (dataPos != testSize) {
562 std::cout << testSize <<
" bytes hole size worked\n";
573 "append a hole to an existing file", {
574 {
"file",
"path to file"
577 void execute(
const std::vector<std::string>& remainingOptions)
override {
578 auto file = remainingOptions.at(0);
579 auto fd =
throwcall::badval(open(file.c_str(), O_WRONLY, 0744), -1,
"open ", file);
580 auto oldFileEnd =
throwcall::badval(lseek(fd, 0, SEEK_END), -1,
"seek end of ", file);
592 "create, unlink, re-create a file", {
593 {
"file",
"path to file"
596 void execute(
const std::vector<std::string>& remainingOptions)
override {
597 auto file = remainingOptions.at(0);
598 auto fd =
throwcall::badval(open(file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0744), -1,
"open ", file);
603 fd =
throwcall::badval(open(file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0744), -1,
"open ", file);
615 "close, move, update a file", {
616 {
"file",
"path to file"
619 void execute(
const std::vector<std::string>& remainingOptions)
override {
620 auto file = remainingOptions.at(0);
621 std::string movedFile(file);
622 movedFile +=
".moved";
623 auto fd =
throwcall::badval(open(file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0744), -1,
"open ", file);
627 "rename ", file,
" to ", movedFile);
628 struct timespec times[2] = {{0, UTIME_OMIT}, {0, UTIME_NOW}};
630 "change mtime of ", file);
639 "create, unlink, update a file", {
640 {
"file",
"path to file"
643 void execute(
const std::vector<std::string>& remainingOptions)
override {
644 auto file = remainingOptions.at(0);
645 auto fd =
throwcall::badval(open(file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0744), -1,
"open ", file);
656 "read file and compare to content", {
657 {
"file",
"path to file"
660 void execute(
const std::vector<std::string>& remainingOptions)
override {
661 auto file = remainingOptions.at(0);
663 std::string data(
content.size(),
'\0');
665 if (data.compare(
content) != 0) {
666 std::cout <<
"difference betwen content and file found\n";
676 "read file via mmap() and compare to content", {
677 {
"file",
"path to file"
680 void execute(
const std::vector<std::string>& remainingOptions)
override {
681 auto file = remainingOptions.at(0);
684 PROT_READ, MAP_PRIVATE | MAP_POPULATE,
687 "mmap ",
content.size(),
" bytes from ", file);
688 auto data =
static_cast<const char *
>(map);
690 std::cout <<
"difference betwen content and file found\n";
702 "truncate existing file to it;s size", {
703 {
"file",
"path to file"
706 void execute(
const std::vector<std::string>& remainingOptions)
override {
707 auto file = remainingOptions.at(0);
711 throwcall::good0(ftruncate(fd, statbuf.st_size),
"truncate ", file,
" to ", statbuf.st_size,
" bytes");
718 class gpfsPrealloc:
public baseTest {
720 gpfsPrealloc():
baseTest(
"gpfsPrealloc",
721 "write a file with gpfsRealloc", {
722 {
"file",
"path to file"
725 void execute(
const std::vector<std::string>& remainingOptions)
override {
726 auto file = remainingOptions.at(0);
727 auto fd =
throwcall::badval(open(file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0744), -1,
"open ", file);
729 "gpfs_prealloc ", content.size(),
" bytes for ", file);
734 static gpfsPrealloc gpfsPreallocInstance;
740 "write files with ugly names", {
741 {
"dir",
"directory to create the files in"
744 void execute(
const std::vector<std::string>& remainingOptions)
override {
745 auto dir = remainingOptions.at(0);
746 auto dirfd =
throwcall::badval(open(dir.c_str(),O_PATH | O_DIRECTORY),-1,
"can't open ",dir);
747 std::list<std::string> names{
"name with spaces",
748 "name\nwith\nnewlines",
749 "name\rwith\rreturns",
750 "name\r\nwith\r\nCRLF",
751 "name_with_\033[91mred\033[0m_part",
752 "name_with_\xe2\x80\xadleft-to-right\xe2\x80\x8e_part",
753 "name_with_\xe2\x82\xac_euro",
754 "name_with_ɄɳɨȻʘΔε_unicode",
755 "name_with_🤔💩☢_emojis",
756 "name_with_䭫顙_more_unicode"
759 std::list<std::pair<std::string,unsigned char>> ControlChars{
760 {
"SOH", 001}, {
"STX", 002}, {
"ETX", 003}, {
"EOT", 004},
761 {
"ENQ", 005}, {
"ACK", 006},
789 for (
const auto& cc: ControlChars) {
790 std::string ugly(
"name_with_");
791 ugly.push_back(cc.second);
793 ugly+=std::to_string(cc.second);
796 names.push_back(ugly);
800 std::vector<std::pair<unsigned char, unsigned char>> ranges{{32,46},{58,63},{91,96},{123,126}};
801 for (
const auto& range: ranges) {
802 for (
unsigned char c=range.first; c<=range.second; c++) {
803 std::string ugly(
"name_with_");
806 ugly+=std::to_string(c);
807 names.push_back(ugly);
811 for (
unsigned char c=128; c!=0; c++) {
812 std::string ugly(
"name_with_");
815 ugly+=std::to_string(c);
816 names.push_back(ugly);
819 auto nameMax =
throwcall::badval(fpathconf(dirfd,_PC_NAME_MAX),-1,
"can't get max name length");
820 auto ugly = std::to_string(nameMax);
821 ugly.resize(nameMax,
'b');
822 names.push_back(ugly);
825 for (
const auto& ugly: names) {
826 std::string file(dir);
829 auto fd = openat(dirfd,ugly.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0744);
830 if (fd == -1 && errno == EINVAL) {
831 std::cerr <<
"illegal file name \" << ugly << \" tried: ";
832 for (
auto c : ugly) {
833 std::cerr << std::hex << std::setw(2) << c;
835 std::cerr << std::dec <<
"\n";
851 "write files in paths with ugly names", {
852 {
"dir",
"directory to create the paths in"
855 void execute(
const std::vector<std::string>& remainingOptions)
override {
856 auto dir = remainingOptions.at(0);
857 auto dirfd =
throwcall::badval(open(dir.c_str(),O_PATH | O_DIRECTORY),-1,
"can't open ",dir);
858 std::list<std::string> names{
"path with spaces",
859 "path\nwith\nnewlines",
860 "path\rwith\rreturns",
861 "path\r\nwith\r\nCRLF",
862 "path_with_\033[91mred\033[0m_part",
863 "path_with_\xe2\x80\xadleft-to-right\xe2\x80\x8e_part",
864 "path_with_\xe2\x82\xac_euro",
865 "path_with_ɄɳɨȻʘΔε_unicode",
866 "path_with_🤔💩☢_emojis",
867 "path_with_䭫顙_more_unicode"
870 auto nameMax =
throwcall::badval(fpathconf(dirfd,_PC_NAME_MAX),-1,
"can't get max name length");
871 auto ugly = std::to_string(nameMax);
872 ugly.resize(nameMax,
'b');
873 names.push_back(ugly);
876 for (
const auto& ugly: names) {
877 auto retval = mkdirat(dirfd,ugly.c_str(), 0777);
878 if (retval == -1 && errno == EINVAL) {
879 std::cerr <<
"illegal dir name \" << ugly << \" tried \n";
884 std::string file(ugly);
886 auto fd =
throwcall::badval(openat(dirfd, file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0744),
897 int main(
int argc,
const char *argv[]) {
905 action.fAddToRange(test.first);
907 auto remainingOptions = parser.
fParse(argc, argv);
911 test->second->Execute(remainingOptions);
913 std::cerr <<
"unknown action '" << action <<
"'\n";