//////////////////////////////////////////////////////////////////////////// // // Boot Service // // Copyright Microsoft Corporation // #define UNICODE #define _UNICODE #define _WIN32_WINNT 0x500 #include #include #include "wincrypt.h" #include #include #include #include #include "srandom.hpp" #define ARRAYOF(x) (sizeof(x)/sizeof(x[0])) #define ASSERT(a) assert(a) ////////////////////////////////////////////////////////////////////////////// // static SRandom g_Rng; static DWORD64 g_MaxBlock; static DWORD g_BlockBytes; static void ResetOffsetRNG(DWORD64 diskBytes, DWORD blockBytes) { g_Rng.reset(); assert(blockBytes != 0); g_MaxBlock = diskBytes / blockBytes; g_BlockBytes = blockBytes; } static DWORD64 GetRandomOffset() { static_assert(SRandom::Maximum == 0x7fffffff); DWORD64 offset = 0; for (int i = 0; i < 64; i += 30) { offset ^= (DWORD64)(g_Rng.next() << i); } offset &= 0x7fffffffffffffffi64; offset %= g_MaxBlock; assert (offset >= 0 && offset < g_MaxBlock); return offset * g_BlockBytes; } ////////////////////////////////////////////////////////////////////////////// // DWORD64 GetDiskSize(HANDLE hDisk) { DWORD junk; // discard results DISK_GEOMETRY dg; ZeroMemory(&dg, sizeof(dg)); if (!DeviceIoControl(hDisk, // device to be queried IOCTL_DISK_GET_DRIVE_GEOMETRY, // operation to perform NULL, 0, // no input buffer &dg, sizeof(dg), // output buffer &junk, // # bytes returned NULL)) { printf("DeviceIoControl failed: %d\n", GetLastError()); return 0; } return dg.Cylinders.QuadPart * (ULONG)dg.TracksPerCylinder * (ULONG)dg.SectorsPerTrack * (ULONG)dg.BytesPerSector; } DWORD64 GetPartitionSize(HANDLE hDisk) { DWORD junk; // discard results PARTITION_INFORMATION_EX pi; ZeroMemory(&pi, sizeof(pi)); if (!DeviceIoControl(hDisk, // device to be queried IOCTL_DISK_GET_PARTITION_INFO_EX, // operation to perform NULL, 0, // no input buffer &pi, sizeof(pi), // output buffer &junk, // # bytes returned NULL)) { printf("DeviceIoControl failed: %d\n", GetLastError()); return 0; } return pi.PartitionLength.QuadPart; } int DiskReadTest(PCWSTR pwzDisk, DWORD dwBlockSize, DWORD dwBlockCount, DWORD dwLimit, BOOL fRandom, BOOL fWriteThrough) { PBYTE pbBuffer = (PBYTE)VirtualAlloc(NULL, 0x100000, MEM_COMMIT, PAGE_READWRITE); if (pbBuffer == NULL) { printf("VirtualAlloc failed: %d\n", GetLastError()); return 1; } DWORD dwAttributes = FILE_FLAG_NO_BUFFERING; if (fWriteThrough) { dwAttributes |= FILE_FLAG_WRITE_THROUGH; } HANDLE hDisk = CreateFile(pwzDisk, // drive to open GENERIC_READ, // no access to the drive FILE_SHARE_READ | // share mode FILE_SHARE_WRITE, NULL, // default security attributes OPEN_EXISTING, // disposition dwAttributes, NULL); // do not copy file attributes if (hDisk == INVALID_HANDLE_VALUE) // cannot open the drive { printf("CreateFile failed: %d\n", GetLastError()); return 1; } DWORD64 dwDisk = GetPartitionSize(hDisk); // DWORD64 dwDisk = GetDiskSize(hDisk); if (dwLimit != 0) { dwDisk = dwLimit * 1024i64 * 1024i64; } DWORD64 dwTotal = dwBlockCount * dwBlockSize; printf("# Type: %s Read\n", fRandom ? "Random" : "Sequential"); printf("# Disk: %10I64d MB\n", dwDisk / (1024 * 1024)); printf("# Limit: %10d MB\n", dwLimit); printf("# Work: %10.2f MB\n", (float)dwTotal / (1024 * 1024)); printf("# Ops: %10d\n", dwTotal / dwBlockSize); DWORD dwOps = 0; LARGE_INTEGER before; QueryPerformanceCounter(&before); LARGE_INTEGER liPos; if (fRandom) { ResetOffsetRNG(dwDisk, dwBlockSize); liPos.QuadPart = GetRandomOffset(); while (dwOps++ < dwBlockCount) { if (SetFilePointerEx(hDisk, liPos, NULL, FILE_BEGIN) == FALSE) { printf("SetFilePointerEx failed: %d\n", GetLastError()); return 1; } liPos.QuadPart = GetRandomOffset(); DWORD cbRead = 0; if (!ReadFile(hDisk, pbBuffer, dwBlockSize, &cbRead, NULL)) { printf("ReadFile failed: %d\n", GetLastError()); return 1; } } } else { liPos.QuadPart = 0; while (dwOps++ < dwBlockCount) { if (SetFilePointerEx(hDisk, liPos, NULL, FILE_BEGIN) == FALSE) { printf("SetFilePointerEx failed: %d\n", GetLastError()); return 1; } liPos.QuadPart += dwBlockSize; DWORD cbRead; if (!ReadFile(hDisk, pbBuffer, dwBlockSize, &cbRead, NULL)) { printf("ReadFile failed: %d\n", GetLastError()); return 1; } } } LARGE_INTEGER after; QueryPerformanceCounter(&after); LARGE_INTEGER frequency; QueryPerformanceFrequency(&frequency); CloseHandle(hDisk); after.QuadPart -= before.QuadPart; //printf("Delta : %18I64d\n", after.QuadPart); //printf("Freque: %18I64d\n", frequency.QuadPart); double elapsed = (double)after.QuadPart / (double)frequency.QuadPart; printf("%cR.%d ", fRandom ? 'R' : 'S', dwBlockSize); printf("Mbps %10.3f Ops/Sec: %10.3f Elapsed %10.3f Check 0x%I64x\n", ((double)dwTotal) / (1024 * 1024 * elapsed), (double)dwOps / elapsed, elapsed, liPos.QuadPart); return 0; } int DiskWriteTest(PCWSTR pwzDisk, DWORD dwBlockSize, DWORD dwBlockCount, DWORD dwLimit, BOOL fRandom, BOOL fWriteThrough) { PBYTE pbBuffer = (PBYTE)VirtualAlloc(NULL, 0x100000, MEM_COMMIT, PAGE_READWRITE); if (pbBuffer == NULL) { printf("VirtualAlloc failed: %d\n", GetLastError()); return 1; } DWORD dwAttributes = FILE_FLAG_NO_BUFFERING; if (fWriteThrough) { dwAttributes |= FILE_FLAG_WRITE_THROUGH; } HANDLE hDisk = CreateFile(pwzDisk, // drive to open GENERIC_WRITE | GENERIC_READ, 0, NULL, // default security attributes OPEN_EXISTING, // disposition dwAttributes, NULL); // do not copy file attributes if (hDisk == INVALID_HANDLE_VALUE) // cannot open the drive { printf("CreateFile failed: %d\n", GetLastError()); return 1; } DWORD64 dwDisk = GetPartitionSize(hDisk); if (dwLimit != 0) { dwDisk = dwLimit * 1024i64 * 1024i64; } DWORD64 dwTotal = dwBlockCount * dwBlockSize; printf("# Type: %s Write\n", fRandom ? "Random" : "Sequential"); printf("# Disk: %10I64d MB\n", dwDisk / (1024 * 1024)); printf("# Work: %10.2f MB\n", (float)dwTotal / (1024 * 1024)); printf("# Ops: %10d\n", dwTotal / dwBlockSize); DWORD dwOps = 0; LARGE_INTEGER before; QueryPerformanceCounter(&before); LARGE_INTEGER liPos; if (fRandom) { ResetOffsetRNG(dwDisk, dwBlockSize); liPos.QuadPart = GetRandomOffset(); while (dwOps++ < dwBlockCount) { if (SetFilePointerEx(hDisk, liPos, NULL, FILE_BEGIN) == FALSE) { printf("SetFilePointerEx failed: %d\n", GetLastError()); return 1; } liPos.QuadPart = GetRandomOffset(); DWORD cbRead = 0; if (!WriteFile(hDisk, pbBuffer, dwBlockSize, &cbRead, NULL)) { printf("WriteFile failed: %d\n", GetLastError()); return 1; } } } else { liPos.QuadPart = 0; while (dwOps++ < dwBlockCount) { if (SetFilePointerEx(hDisk, liPos, NULL, FILE_BEGIN) == FALSE) { printf("SetFilePointerEx failed: %d\n", GetLastError()); return 1; } liPos.QuadPart += dwBlockSize; DWORD cbRead; if (!WriteFile(hDisk, pbBuffer, dwBlockSize, &cbRead, NULL)) { printf("WriteFile failed: %d\n", GetLastError()); return 1; } } } LARGE_INTEGER after; QueryPerformanceCounter(&after); LARGE_INTEGER frequency; QueryPerformanceFrequency(&frequency); CloseHandle(hDisk); after.QuadPart -= before.QuadPart; //printf("Delta : %18I64d\n", after.QuadPart); //printf("Freque: %18I64d\n", frequency.QuadPart); double elapsed = (double)after.QuadPart / (double)frequency.QuadPart; printf("%cW.%d ", fRandom ? 'R' : 'S', dwBlockSize); printf("Mbps: %10.3f Ops/Sec: %10.3f Elapsed: %10.3f Check: 0x%I64x\n", ((double)dwTotal) / (1024 * 1024 * elapsed), (double)dwOps / elapsed, elapsed, liPos.QuadPart); return 0; } ////////////////////////////////////////////////////////////////////////////// // int __cdecl wmain(int argc, WCHAR **argv) { BOOL fNeedHelp = FALSE; BOOL fRead = FALSE; BOOL fWrite = FALSE; DWORD dwBlock = 512; DWORD dwMegabytes = 0; DWORD dwBlockCount = 0; BOOL fRandom = FALSE; BOOL fWriteThrough = TRUE; DWORD dwLimit = 0; WCHAR wzPartition[64] = L""; for (int arg = 1; arg < argc && !fNeedHelp; arg++) { if (argv[arg][0] == '-' || argv[arg][0] == '/') { WCHAR *argn = argv[arg]+1; // Argument name WCHAR *argp = argn; // Argument parameter while (*argp && *argp != ':') { argp++; } if (*argp == ':') *argp++ = '\0'; switch (argn[0]) { case '?': // Help fNeedHelp = TRUE; break; case 'b': // Block size case 'B': dwBlock = (DWORD)_wtoi(argp); break; case 'l': // Disk Limit Megabytes case 'L': dwLimit = (DWORD)_wtoi(argp); break; case 'm': // Megabytes of Data case 'M': dwMegabytes = (DWORD)_wtoi(argp); break; case 'n': // Number of blocks case 'N': dwBlockCount = (DWORD)_wtoi(argp); break; case 'r': // Read case 'R': fRead = TRUE; break; case 'w': // Write case 'W': fWrite = TRUE; break; case 'x': // Xtremely Random case 'X': fRandom = TRUE; break; case 'z': case 'Z': fWriteThrough = FALSE; break; default: printf("Unknown argument: %ls\n", argv[arg]); fNeedHelp = TRUE; break; } } else { wsprintf(wzPartition, L"\\\\.\\%ls", argv[arg]); } } if (dwBlock < 512 || dwBlock & (dwBlock - 1)) { fprintf(stderr, "Block size needs to be a power of 2 greater than or equal to 512.\n"); fNeedHelp = true; } if ((dwBlockCount == 0 && dwMegabytes == 0) || (dwBlockCount != 0 && dwMegabytes != 0)) { fprintf(stderr, "Need to specify either number of blocks or number of megabytes for I/O.\n"); fNeedHelp = true; } if (dwMegabytes > 0) { DWORD64 tmp = (dwMegabytes * 1048576i64) / dwBlock; dwBlockCount = (DWORD) tmp; } if (argc == 1) { fNeedHelp = TRUE; } if (fNeedHelp) { printf( "Usage:\n" " diskrw [options] {drive letter:}\n" "Options:\n" " /b:N -- Block size (in bytes).\n" " /l:M -- Limit (defaults to full partition) in megabytes.\n" " /m:M -- Megabytes to read or write.\n" " /n:N -- Number of blocks to read or write.\n" " /x -- Random, otherwise, defaults to sequential.\n" " /z -- No write through, defaults to write through.\n" " /r -- Read.\n" " /w -- Write.\n" " /? -- Display this help screen.\n" "Example:\n" " diskrw /r /x /b:512 c:\n" ); return 1; } SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); printf("# Disk: %ls\n", wzPartition); if (fRead) { DiskReadTest(wzPartition, dwBlock, dwBlockCount, dwLimit, fRandom, fWriteThrough); } else if (fWrite){ DiskWriteTest(wzPartition, dwBlock, dwBlockCount, dwLimit, fRandom, fWriteThrough); } return 0; } // ///////////////////////////////////////////////////////////////// End of File.