/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // This file contains implementation of a test application that tests // functionality of AdbWinApi interface. In this test we will use AdbWinApi // interface in order to enumerate USB interfaces for Android ADB class, and // for each interface found we will test USB I/O on that interface by sending // a simple "hand shake" message to the device connected via this interface. #include "stdafx.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // Android ADB interface identifier const GUID kAdbInterfaceId = ANDROID_USB_CLASS_ID; // Number of interfaces detected in TestEnumInterfaces. int interface_count = 0; // Constants used to initialize a "handshake" message #define MAX_PAYLOAD 4096 #define A_SYNC 0x434e5953 #define A_CNXN 0x4e584e43 #define A_OPEN 0x4e45504f #define A_OKAY 0x59414b4f #define A_CLSE 0x45534c43 #define A_WRTE 0x45545257 #define A_AUTH 0x48545541 #define A_VERSION 0x01000000 // AUTH packets first argument #define ADB_AUTH_TOKEN 1 #define ADB_AUTH_SIGNATURE 2 #define ADB_AUTH_RSAPUBLICKEY 3 // Interface descriptor constants for ADB interface #define ADB_CLASS 0xff #define ADB_SUBCLASS 0x42 #define ADB_PROTOCOL 0x1 // Formats message sent to USB device struct message { unsigned int command; /* command identifier constant */ unsigned int arg0; /* first argument */ unsigned int arg1; /* second argument */ unsigned int data_length; /* length of payload (0 is allowed) */ unsigned int data_crc32; /* crc32 of data payload */ unsigned int magic; /* command ^ 0xffffffff */ }; // // Test routines declarations. // // Tests interface enumeration. bool TestEnumInterfaces(); // Tests all interfaces detected for our device class. bool TestInterfaces(); // Tests interface addressed by the given device name. bool TestInterface(const wchar_t* device_name); // Tests interface opened with ADB API. bool TestInterfaceHandle(ADBAPIHANDLE interface_handle); // Sends a "handshake" message to the given interface. bool DeviceHandShake(ADBAPIHANDLE adb_interface); // Test AdbCloseHandle race condition. bool TestCloseRaceCondition(); int __cdecl _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { // Test enum interfaces. if (!TestEnumInterfaces()) return -1; if (0 == interface_count) { printf("\nNo ADB interfaces found. Make sure that device is " "connected to USB port and is powered on."); return 1; } // Test each interface found in the system if (!TestInterfaces()) return -2; // Test for AdbCloseHandle race condition if (!TestCloseRaceCondition()) return -3; return 0; } bool TestEnumInterfaces() { // Enumerate interfaces ADBAPIHANDLE enum_handle = AdbEnumInterfaces(kAdbInterfaceId, true, true, true); if (NULL == enum_handle) { printf("\nEnum interfaces failure:"); printf("\nUnable to enumerate ADB interfaces: %u", GetLastError()); return false; } // Unite interface info structure and buffer big enough to contain the // largest structure. union { AdbInterfaceInfo interface_info; char buf[4096]; }; unsigned long buf_size = sizeof(buf); // Enumerate (and count) interfaces, printing information for each found // interface. interface_count = 0; while (AdbNextInterface(enum_handle, &interface_info, &buf_size)) { interface_count++; printf("\nFound interface %ws:", interface_info.device_name); if (interface_info.flags & SPINT_ACTIVE) printf(" ACTIVE"); if (interface_info.flags & SPINT_DEFAULT) printf(" DEFAULT"); if (interface_info.flags & SPINT_REMOVED) printf(" REMOVED"); buf_size = sizeof(buf); } bool ret = true; if (GetLastError() != ERROR_NO_MORE_ITEMS) { printf("\n--- AdbNextInterface failure %u", GetLastError()); ret = false; } if (!AdbCloseHandle(enum_handle)) { printf("\n--- AdbCloseHandle failure %u", GetLastError()); ret = false; } return ret; } bool TestInterfaces() { bool ret = true; // Enumerate interfaces ADBAPIHANDLE enum_handle = AdbEnumInterfaces(kAdbInterfaceId, true, true, true); if (NULL == enum_handle) { printf("\nTest interfaces failure:"); printf("\nUnable to enumerate ADB interfaces: %u", GetLastError()); ret = false; } else { // Unite interface info structure and buffer big enough to contain the // largest structure. union { AdbInterfaceInfo interface_info; char buf[4096]; }; unsigned long buf_size = sizeof(buf); // Test each found interface while (AdbNextInterface(enum_handle, &interface_info, &buf_size)) { TestInterface(interface_info.device_name); buf_size = sizeof(buf); } if (GetLastError() != ERROR_NO_MORE_ITEMS) { printf("\n--- AdbNextInterface failure %u", GetLastError()); ret = false; } if (!AdbCloseHandle(enum_handle)) { printf("\n--- AdbCloseHandle failure %u", GetLastError()); ret = false; } } return ret; } bool TestInterface(const wchar_t* device_name) { printf("\n*** Test interface( %ws )", device_name); // Get ADB handle to the interface by its name ADBAPIHANDLE interface_handle = AdbCreateInterfaceByName(device_name); if (NULL == interface_handle) { printf(" FAILED:\nUnable to create interface by name: %u", GetLastError()); return false; } // Test it TestInterfaceHandle(interface_handle); if (!AdbCloseHandle(interface_handle)) { printf("\n--- AdbCloseHandle failure %u", GetLastError()); return false; } return true; } bool TestInterfaceName(ADBAPIHANDLE interface_handle) { bool ret = true; unsigned long intr_name_size = 0; char* buf = NULL; if (AdbGetInterfaceName(interface_handle, NULL, &intr_name_size, true)) { printf("\n--- AdbGetInterfaceName unexpectedly succeeded %u", GetLastError()); ret = false; goto exit; } if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { printf("\n--- AdbGetInterfaceName failure %u", GetLastError()); ret = false; goto exit; } if (intr_name_size == 0) { printf("\n--- AdbGetInterfaceName returned name size of zero"); ret = false; goto exit; } const size_t buf_size = intr_name_size + 16; // extra in case of overwrite buf = reinterpret_cast(malloc(buf_size)); if (buf == NULL) { printf("\n--- could not malloc %d bytes, errno %u", buf_size, errno); ret = false; goto exit; } const char buf_fill = (unsigned char)0xFF; memset(buf, buf_fill, buf_size); if (!AdbGetInterfaceName(interface_handle, buf, &intr_name_size, true)) { printf("\n--- AdbGetInterfaceName failure %u", GetLastError()); ret = false; goto exit; } if (buf[intr_name_size - 1] != '\0') { printf("\n--- AdbGetInterfaceName returned non-NULL terminated string"); ret = false; goto exit; } for (size_t i = intr_name_size; i < buf_size; ++i) { if (buf[i] != buf_fill) { printf("\n--- AdbGetInterfaceName overwrote past the end of the buffer at" " index %u with 0x%02X", i, (unsigned char)buf[i]); ret = false; goto exit; } } printf("\n+++ Interface name %s", buf); exit: free(buf); return ret; } void DumpEndpointInformation(const AdbEndpointInformation* pipe_info) { printf("\n max_packet_size = %u", pipe_info->max_packet_size); printf("\n max_transfer_size = %u", pipe_info->max_transfer_size); printf("\n endpoint_type = %u", pipe_info->endpoint_type); const char* endpoint_type_desc = NULL; switch (pipe_info->endpoint_type) { #define CASE_TYPE(type) case type: endpoint_type_desc = #type; break CASE_TYPE(AdbEndpointTypeInvalid); CASE_TYPE(AdbEndpointTypeControl); CASE_TYPE(AdbEndpointTypeIsochronous); CASE_TYPE(AdbEndpointTypeBulk); CASE_TYPE(AdbEndpointTypeInterrupt); #undef CASE_TYPE } if (endpoint_type_desc != NULL) { printf(" (%s)", endpoint_type_desc); } printf("\n endpoint_address = %02X", pipe_info->endpoint_address); printf("\n polling_interval = %u", pipe_info->polling_interval); printf("\n setting_index = %u", pipe_info->setting_index); } bool TestInterfaceHandle(ADBAPIHANDLE interface_handle) { // Get interface name. if (!TestInterfaceName(interface_handle)) { return false; } // Get device descriptor for the interface USB_DEVICE_DESCRIPTOR dev_desc; if (AdbGetUsbDeviceDescriptor(interface_handle, &dev_desc)) { printf("\n+++ Device descriptor:"); printf("\n bLength = %u", dev_desc.bLength); printf("\n bDescriptorType = %u", dev_desc.bDescriptorType); printf("\n bcdUSB = %u", dev_desc.bcdUSB); printf("\n bDeviceClass = %u", dev_desc.bDeviceClass); printf("\n bDeviceSubClass = %u", dev_desc.bDeviceSubClass); printf("\n bDeviceProtocol = %u", dev_desc.bDeviceProtocol); printf("\n bMaxPacketSize0 = %u", dev_desc.bMaxPacketSize0); printf("\n idVendor = %X", dev_desc.idVendor); printf("\n idProduct = %X", dev_desc.idProduct); printf("\n bcdDevice = %u", dev_desc.bcdDevice); printf("\n iManufacturer = %u", dev_desc.iManufacturer); printf("\n iProduct = %u", dev_desc.iProduct); printf("\n iSerialNumber = %u", dev_desc.iSerialNumber); printf("\n bNumConfigurations = %u", dev_desc.bNumConfigurations); } else { printf("\n--- AdbGetUsbDeviceDescriptor failure %u", GetLastError()); return false; } // Get configuration descriptor for the interface USB_CONFIGURATION_DESCRIPTOR config_desc; if (AdbGetUsbConfigurationDescriptor(interface_handle, &config_desc)) { printf("\n+++ Configuration descriptor:"); printf("\n bLength = %u", config_desc.bLength); printf("\n bDescriptorType = %u", config_desc.bDescriptorType); printf("\n wTotalLength = %u", config_desc.wTotalLength); printf("\n bNumInterfaces = %u", config_desc.bNumInterfaces); printf("\n bConfigurationValue = %u", config_desc.bConfigurationValue); printf("\n iConfiguration = %u", config_desc.iConfiguration); printf("\n bmAttributes = %u", config_desc.bmAttributes); printf("\n MaxPower = %u", config_desc.MaxPower); } else { printf("\n--- AdbGetUsbConfigurationDescriptor failure %u", GetLastError()); return false; } // Get device serial number char ser_num[1024]; unsigned long ser_num_size = sizeof(ser_num); if (AdbGetSerialNumber(interface_handle, ser_num, &ser_num_size, true)) { printf("\n+++ Serial number: %s", ser_num); } else { printf("\n--- AdbGetSerialNumber failure %u", GetLastError()); return false; } // Get interface descriptor USB_INTERFACE_DESCRIPTOR intr_desc; if (AdbGetUsbInterfaceDescriptor(interface_handle, &intr_desc)) { printf("\n+++ Interface descriptor:"); printf("\n bDescriptorType = %u", intr_desc.bDescriptorType); printf("\n bInterfaceNumber = %u", intr_desc.bInterfaceNumber); printf("\n bAlternateSetting = %u", intr_desc.bAlternateSetting); printf("\n bNumEndpoints = %u", intr_desc.bNumEndpoints); printf("\n bInterfaceClass = %u", intr_desc.bInterfaceClass); if (intr_desc.bInterfaceClass == ADB_CLASS) { printf(" (ADB_CLASS)"); } printf("\n bInterfaceSubClass = %u", intr_desc.bInterfaceSubClass); if (intr_desc.bInterfaceSubClass == ADB_SUBCLASS) { printf(" (ADB_SUBCLASS)"); } printf("\n bInterfaceProtocol = %u", intr_desc.bInterfaceProtocol); if (intr_desc.bInterfaceProtocol == ADB_PROTOCOL) { printf(" (ADB_PROTOCOL)"); } printf("\n iInterface = %u", intr_desc.iInterface); } else { printf("\n--- AdbGetUsbInterfaceDescriptor failure %u", GetLastError()); return false; } // Enumerate interface's endpoints AdbEndpointInformation pipe_info; for (UCHAR pipe = 0; pipe < intr_desc.bNumEndpoints; pipe++) { if (AdbGetEndpointInformation(interface_handle, pipe, &pipe_info)) { printf("\n PIPE %u info:", pipe); DumpEndpointInformation(&pipe_info); } else { printf("\n--- AdbGetEndpointInformation(%u) failure %u", pipe, GetLastError()); return false; } } // Get default bulk read endpoint info if (AdbGetDefaultBulkReadEndpointInformation(interface_handle, &pipe_info)) { printf("\n Default Bulk Read Pipe info:"); DumpEndpointInformation(&pipe_info); } else { printf("\n--- AdbGetDefaultBulkReadEndpointInformation failure %u", GetLastError()); return false; } // Get default bulk write endpoint info if (AdbGetDefaultBulkWriteEndpointInformation(interface_handle, &pipe_info)) { printf("\n Default Bulk Write Pipe info:"); DumpEndpointInformation(&pipe_info); } else { printf("\n--- AdbGetDefaultBulkWriteEndpointInformation failure %u", GetLastError()); return false; } // Test a handshake on that interface DeviceHandShake(interface_handle); return true; } void HexDump(const void* data, const size_t read_bytes) { const unsigned char* buf = reinterpret_cast(data); const size_t line_length = 16; for (size_t n = 0; n < read_bytes; n += line_length) { const unsigned char* line = &buf[n]; const size_t max_line = min(line_length, read_bytes - n); printf("\n "); for (size_t i = 0; i < line_length; ++i) { if (i >= max_line) { printf(" "); } else { printf("%02X ", line[i]); } } printf(" "); for (size_t i = 0; i < max_line; ++i) { if (isprint(line[i])) { printf("%c", line[i]); } else { printf("."); } } } } void DumpMessageArg0(unsigned int command, unsigned int arg0) { if (command == A_AUTH) { const char* desc = NULL; switch (arg0) { #define CASE_ARG0(arg) case arg: desc = # arg; break CASE_ARG0(ADB_AUTH_TOKEN); CASE_ARG0(ADB_AUTH_SIGNATURE); CASE_ARG0(ADB_AUTH_RSAPUBLICKEY); #undef CASE_ARG0 } if (desc != NULL) { printf(" (%s)", desc); } } } bool DeviceHandShake(ADBAPIHANDLE adb_interface) { // Get interface name char interf_name[512]; unsigned long name_size = sizeof(interf_name); if (!AdbGetInterfaceName(adb_interface, interf_name, &name_size, true)) { printf("\nDeviceHandShake: AdbGetInterfaceName returned error %u", GetLastError()); return false; } printf("\n\nDeviceHandShake on %s", interf_name); char* ser_num = NULL; name_size = 0; if (!AdbGetSerialNumber(adb_interface, ser_num, &name_size, true)) { ser_num = reinterpret_cast(malloc(name_size)); if (NULL != ser_num) { if (!AdbGetSerialNumber(adb_interface, ser_num, &name_size, true)) { printf("\n AdbGetSerialNumber returned error %u", GetLastError()); AdbCloseHandle(adb_interface); return false; } printf("\nInterface serial number is %s", ser_num); free(ser_num); } } // Get default read endpoint ADBAPIHANDLE adb_read = AdbOpenDefaultBulkReadEndpoint(adb_interface, AdbOpenAccessTypeReadWrite, AdbOpenSharingModeReadWrite); if (NULL == adb_read) { printf("\n AdbOpenDefaultBulkReadEndpoint returned error %u", GetLastError()); return false; } // Get default write endpoint ADBAPIHANDLE adb_write = AdbOpenDefaultBulkWriteEndpoint(adb_interface, AdbOpenAccessTypeReadWrite, AdbOpenSharingModeReadWrite); if (NULL == adb_write) { printf("\n AdbOpenDefaultBulkWriteEndpoint returned error %u", GetLastError()); AdbCloseHandle(adb_read); return false; } // Send connect message message msg_send; msg_send.command = A_CNXN; msg_send.arg0 = A_VERSION; msg_send.arg1 = MAX_PAYLOAD; msg_send.data_length = 0; msg_send.data_crc32 = 0; msg_send.magic = msg_send.command ^ 0xffffffff; ULONG written_bytes = 0; bool write_res = AdbWriteEndpointSync(adb_write, &msg_send, sizeof(msg_send), &written_bytes, 500); if (!write_res) { printf("\n AdbWriteEndpointSync returned error %u", GetLastError()); AdbCloseHandle(adb_write); AdbCloseHandle(adb_read); return false; } // Receive handshake message msg_rcv; ULONG read_bytes = 0; bool read_res = AdbReadEndpointSync(adb_read, &msg_rcv, sizeof(msg_rcv), &read_bytes, 512); if (!read_res) { printf("\n AdbReadEndpointSync returned error %u", GetLastError()); AdbCloseHandle(adb_write); AdbCloseHandle(adb_read); return false; } printf("\n Read handshake: %u bytes received", read_bytes); char* cmd_ansi = reinterpret_cast(&msg_rcv.command); printf("\n command = %08X (%c%c%c%c)", msg_rcv.command, cmd_ansi[0], cmd_ansi[1], cmd_ansi[2], cmd_ansi[3]); printf("\n arg0 = %08X", msg_rcv.arg0); DumpMessageArg0(msg_rcv.command, msg_rcv.arg0); printf("\n arg1 = %08X", msg_rcv.arg1); printf("\n data_length = %u", msg_rcv.data_length); printf("\n data_crc32 = %08X", msg_rcv.data_crc32); printf("\n magic = %08X", msg_rcv.magic); printf(" (%s)", (msg_rcv.magic == (msg_rcv.command ^ 0xffffffff)) ? "valid" : "invalid"); if (0 != msg_rcv.data_length) { char* buf = reinterpret_cast(malloc(msg_rcv.data_length)); read_res = AdbReadEndpointSync(adb_read, buf, msg_rcv.data_length, &read_bytes, 512); if (!read_res) { printf("\n AdbReadEndpointSync (data) returned error %u", GetLastError()); free(buf); AdbCloseHandle(adb_write); AdbCloseHandle(adb_read); return false; } HexDump(buf, read_bytes); free(buf); } if (!AdbCloseHandle(adb_write)) { printf("\n--- AdbCloseHandle failure %u", GetLastError()); } if (!AdbCloseHandle(adb_read)) { printf("\n--- AdbCloseHandle failure %u", GetLastError()); } return true; } // Randomly delay the current thread. class RandomDelayer { public: // Prepare for a call to Delay() by getting random data. This call might grab // locks, causing serialization, so this should be called before // time-sensitive code. void SeedRandom() { r_ = rand(); } // Randomly delay the current thread based on a previous call to SeedRandom(). void Delay() { switch (r_ % 5) { case 0: Sleep(0); // Give up time slice to another read-to-run thread. break; case 1: // Try to sleep for 1 ms, but probably more based on OS scheduler // minimum granularity. Sleep(1); break; case 2: // Yield to another thread ready-to-run on the current processor. SwitchToThread(); break; case 3: // Busy-wait for a random amount of time. for (int i = 0; i < r_; ++i) { GetLastError(); } break; case 4: break; // Do nothing, no delay. } } private: int r_; }; volatile ADBAPIHANDLE g_read_handle; volatile ADBAPIHANDLE g_interface_handle; volatile bool g_stop_close_race_thread; unsigned __stdcall CloseRaceThread(void*) { RandomDelayer r; while (!g_stop_close_race_thread) { r.SeedRandom(); // Do volatile reads of both globals ADBAPIHANDLE read_handle = g_read_handle; ADBAPIHANDLE interface_handle = g_interface_handle; // If we got both handles, close them and clear the globals if (read_handle != NULL && interface_handle != NULL) { // Delay random amount before calling the API that conflicts with // Adb{Read,Write}EndpointSync(). r.Delay(); if (!AdbCloseHandle(read_handle)) { printf("\nAdbCloseHandle(read) failure: %u", GetLastError()); } if (!AdbCloseHandle(interface_handle)) { printf("\nAdbCloseHandle(interface) failure: %u", GetLastError()); } // Clear globals so that read thread is free to set them. g_read_handle = NULL; g_interface_handle = NULL; } } return 0; } #define EXPECTED_ERROR_LIST(FOR_EACH) \ FOR_EACH(ERROR_INVALID_HANDLE) \ FOR_EACH(ERROR_HANDLES_CLOSED) \ FOR_EACH(ERROR_OPERATION_ABORTED) #define MAKE_ARRAY_ITEM(x) x, const DWORD g_expected_errors[] = { EXPECTED_ERROR_LIST(MAKE_ARRAY_ITEM) }; #undef MAKE_ARRAY_ITEM #define MAKE_STRING_ITEM(x) #x, const char* g_expected_error_strings[] = { EXPECTED_ERROR_LIST(MAKE_STRING_ITEM) }; #undef MAKE_STRING_ITEM std::string get_error_description(const DWORD err) { const DWORD* end = g_expected_errors + ARRAYSIZE(g_expected_errors); const DWORD* found = std::find(g_expected_errors, end, err); if (found != end) { return g_expected_error_strings[found - g_expected_errors]; } else { char buf[64]; _snprintf(buf, sizeof(buf), "%u", err); return std::string(buf); } } bool is_expected_error(const DWORD err) { const DWORD* end = g_expected_errors + ARRAYSIZE(g_expected_errors); return std::find(g_expected_errors, end, err) != end; } // Test to reproduce https://code.google.com/p/android/issues/detail?id=161890 bool TestCloseRaceCondition() { const DWORD test_duration_sec = 10; printf("\nTesting close race condition for %u seconds... ", test_duration_sec); ADBAPIHANDLE enum_handle = AdbEnumInterfaces(kAdbInterfaceId, true, true, true); if (NULL == enum_handle) { printf("\nUnable to enumerate ADB interfaces: %u", GetLastError()); return false; } union { AdbInterfaceInfo interface_info; char buf[4096]; }; unsigned long buf_size = sizeof(buf); // Get the first interface if (!AdbNextInterface(enum_handle, &interface_info, &buf_size)) { printf("\n--- AdbNextInterface failure %u", GetLastError()); return false; } if (!AdbCloseHandle(enum_handle)) { printf("\nAdbCloseHandle(enum_handle) failure: %u", GetLastError()); } HANDLE thread_handle = reinterpret_cast( _beginthreadex(NULL, 0, CloseRaceThread, NULL, 0, NULL)); if (thread_handle == NULL) { printf("\n--- _beginthreadex failure %u", errno); return false; } // Run the test for 10 seconds. It usually reproduces the crash in 1 second. const DWORD tick_start = GetTickCount(); const DWORD test_duration_ticks = test_duration_sec * 1000; RandomDelayer r; std::map read_errors; while (GetTickCount() < tick_start + test_duration_ticks) { // Busy-wait until close thread has cleared the handles, so that we don't // leak handles during the test. while (g_read_handle != NULL) {} while (g_interface_handle != NULL) {} ADBAPIHANDLE interface_handle = AdbCreateInterfaceByName( interface_info.device_name); if (interface_handle == NULL) { // Not really expected to encounter an error here. printf("\n--- AdbCreateInterfaceByName failure %u", GetLastError()); continue; // try again } ADBAPIHANDLE read_handle = AdbOpenDefaultBulkReadEndpoint( interface_handle, AdbOpenAccessTypeReadWrite, AdbOpenSharingModeReadWrite); if (read_handle == NULL) { // Not really expected to encounter an error here, so report, cleanup, // and retry. printf("\n--- AdbOpenDefaultBulkReadEndpoint failure %u", GetLastError()); AdbCloseHandle(interface_handle); continue; } r.SeedRandom(); // Set handles to allow other thread to close them. g_read_handle = read_handle; g_interface_handle = interface_handle; // Delay random amount before calling the API that conflicts with // AdbCloseHandle(). r.Delay(); message msg_rcv; ULONG read_bytes = 0; while (AdbReadEndpointSync(read_handle, &msg_rcv, sizeof(msg_rcv), &read_bytes, 0 /* infinite timeout */)) { // Keep reading until a crash or we're broken out of the read // (with an error) by the CloseRaceThread. } read_errors[GetLastError()]++; } g_stop_close_race_thread = true; if (WaitForSingleObject(thread_handle, INFINITE) != WAIT_OBJECT_0) { printf("\n--- WaitForSingleObject failure %u", GetLastError()); } if (!CloseHandle(thread_handle)) { printf("\n--- CloseHandle failure %u", GetLastError()); } // The expected errors are the errors that would be encountered if the code // had all the major concurrent interleavings. So the test only passes if // we encountered all the expected errors, and thus stress tested all the // possible major concurrent interleavings. bool pass = true; for (size_t i = 0; i < ARRAYSIZE(g_expected_errors); ++i) { // If we didn't encounter the expected error code, then the test failed. if (read_errors.count(g_expected_errors[i]) == 0) { pass = false; break; } } if (pass) { printf("passed"); } else { printf("failed."); printf("\nPerhaps you just need to run the test longer or again."); } printf("\nRead Error Code\t\tCount"); printf("\n============================="); for (std::map::iterator it = read_errors.begin(); it != read_errors.end(); ++it) { printf("\n%s\t%u%s", get_error_description(it->first).c_str(), it->second, is_expected_error(it->first) ? " (expected)" : ""); } for (size_t i = 0; i < ARRAYSIZE(g_expected_errors); ++i) { if (read_errors.count(g_expected_errors[i]) == 0) { printf("\n%s\t%u (was not encountered, but was expected)", get_error_description(g_expected_errors[i]).c_str(), 0); } } return pass; }