From bbf8b1193bedcdb84f51023f7e02f6470218732d Mon Sep 17 00:00:00 2001 From: flobernd Date: Mon, 3 Jul 2017 21:10:04 +0200 Subject: [PATCH] Added performance test --- CMakeLists.txt | 5 + include/Zydis/Decoder.h | 16 ++- src/Decoder.c | 32 ++--- tools/PerfTest.c | 303 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 335 insertions(+), 21 deletions(-) create mode 100644 tools/PerfTest.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 9116845..f8c01ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -145,4 +145,9 @@ if (ZYDIS_BUILD_TOOLS) target_link_libraries("ZydisFuzzIn" "Zydis") set_target_properties("ZydisFuzzIn" PROPERTIES FOLDER "Tools") target_compile_definitions("ZydisFuzzIn" PRIVATE "_CRT_SECURE_NO_WARNINGS") + + add_executable("PerfTest" "tools/PerfTest.c") + target_link_libraries("PerfTest" "Zydis") + set_target_properties("PerfTest" PROPERTIES FOLDER "Tools") + target_compile_definitions("PerfTest" PRIVATE "_CRT_SECURE_NO_WARNINGS") endif () diff --git a/include/Zydis/Decoder.h b/include/Zydis/Decoder.h index 08db3f4..bfd5345 100644 --- a/include/Zydis/Decoder.h +++ b/include/Zydis/Decoder.h @@ -52,10 +52,20 @@ enum ZydisDecodeGranularities { ZYDIS_DECODE_GRANULARITY_DEFAULT, /** - * @brief Minimal instruction decoding without semantic operand analysis. + * @brief Minimal instruction decoding without semantic analysis. + * + * This mode should be sufficient, if you plan to analyse code for pure relocation purposes, + * as it gives you access to the mnemonic, the instruction-length, displacements, immediates + * and even the `ZYDIS_ATTRIB_IS_RELATIVE`. + * + * Operands, most attributes and other specific information (like AVX info) are not + * accessible in this mode. */ - ZYDIS_DECODE_GRANULARITY_MINIMAL, - ZYDIS_DECODE_GRANULARITY_FULL + ZYDIS_DECODE_GRANULARITY_PHYSICAL, + /** + * @brief Full physical and semantical instruction-decoding. + */ + ZYDIS_DECODE_GRANULARITY_SEMANTIC }; /** diff --git a/src/Decoder.c b/src/Decoder.c index 504063c..5835915 100644 --- a/src/Decoder.c +++ b/src/Decoder.c @@ -213,9 +213,6 @@ enum ZydisRegisterEncodings * @param value A pointer to the memory that receives the byte from the input data-source. * * @return A zydis status code. - * - * If not empty, the internal buffer of the @c ZydisDecoderContext instance is used as temporary - * data-source, instead of reading the byte from the actual input data-source. * * This function may fail, if the @c ZYDIS_MAX_INSTRUCTION_LENGTH limit got exceeded, or no more * data is available. @@ -249,10 +246,7 @@ static ZydisStatus ZydisInputPeek(ZydisDecoderContext* context, * * This function is supposed to get called ONLY after a successfull call of @c ZydisInputPeek. * - * If not empty, the read-position of the @c ZydisDecoderContext instances internal buffer is - * increased, instead of the actual input data-sources read-position. - * - * This function increases the @c length field of the @c ZydisInstructionInfo struct by one and + * This function increases the @c length field of the @c ZydisDecodedInstruction struct by one and * adds the current byte to the @c data array. */ static void ZydisInputSkip(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction) @@ -3140,7 +3134,7 @@ static ZydisStatus ZydisCollectOptionalPrefixes(ZydisDecoderContext* context, * * @return A zydis status code. */ -static ZydisStatus ZydisDecodeOptionalInstructionParts(ZydisDecoderContext* context, +static ZydisStatus ZydisDecodeInstructionPhysical(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction, const ZydisInstructionParts* optionalParts) { ZYDIS_ASSERT(context); @@ -3232,7 +3226,7 @@ static ZydisStatus ZydisDecodeOptionalInstructionParts(ZydisDecoderContext* cont if (optionalParts->flags & ZYDIS_INSTRPART_FLAG_HAS_DISP) { ZYDIS_CHECK(ZydisReadDisplacement( - context, instruction, optionalParts->disp.size[context->easzIndex])); + context, instruction, optionalParts->disp.size[context->easzIndex])); } if (optionalParts->flags & ZYDIS_INSTRPART_FLAG_HAS_IMM0) @@ -3243,7 +3237,7 @@ static ZydisStatus ZydisDecodeOptionalInstructionParts(ZydisDecoderContext* cont } ZYDIS_CHECK(ZydisReadImmediate(context, instruction, 0, optionalParts->imm[0].size[context->eoszIndex], - optionalParts->imm[0].isSigned, optionalParts->imm[0].isRelative)); + optionalParts->imm[0].isSigned, optionalParts->imm[0].isRelative)); } if (optionalParts->flags & ZYDIS_INSTRPART_FLAG_HAS_IMM1) @@ -3251,7 +3245,7 @@ static ZydisStatus ZydisDecodeOptionalInstructionParts(ZydisDecoderContext* cont ZYDIS_ASSERT(!(optionalParts->flags & ZYDIS_INSTRPART_FLAG_HAS_DISP)); ZYDIS_CHECK(ZydisReadImmediate(context, instruction, 1, optionalParts->imm[1].size[context->eoszIndex], - optionalParts->imm[1].isSigned, optionalParts->imm[1].isRelative)); + optionalParts->imm[1].isSigned, optionalParts->imm[1].isRelative)); } return ZYDIS_STATUS_SUCCESS; @@ -4266,8 +4260,7 @@ static ZydisStatus ZydisDecodeInstruction(ZydisDecoderContext* context, const ZydisInstructionParts* optionalParts; ZydisGetOptionalInstructionParts(node, &optionalParts); - ZYDIS_CHECK( - ZydisDecodeOptionalInstructionParts(context, instruction, optionalParts)); + ZYDIS_CHECK(ZydisDecodeInstructionPhysical(context, instruction, optionalParts)); if (instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_3DNOW) { @@ -4291,7 +4284,7 @@ static ZydisStatus ZydisDecodeInstruction(ZydisDecoderContext* context, instruction->mnemonic = definition->mnemonic; - if (context->decoder->decodeGranularity == ZYDIS_DECODE_GRANULARITY_FULL) + if (context->decoder->decodeGranularity == ZYDIS_DECODE_GRANULARITY_SEMANTIC) { ZydisSetPrefixRelatedAttributes(context, instruction, definition); switch (instruction->encoding) @@ -4335,14 +4328,17 @@ ZydisStatus ZydisDecoderInitEx(ZydisDecoder* decoder, ZydisMachineMode machineMo { if (!decoder || ((machineMode != 16) && (machineMode != 32) && (machineMode != 64)) || ((decodeGranularity != ZYDIS_DECODE_GRANULARITY_DEFAULT) && - (decodeGranularity != ZYDIS_DECODE_GRANULARITY_FULL) && - (decodeGranularity != ZYDIS_DECODE_GRANULARITY_MINIMAL))) + (decodeGranularity != ZYDIS_DECODE_GRANULARITY_PHYSICAL) && + (decodeGranularity != ZYDIS_DECODE_GRANULARITY_SEMANTIC))) { return ZYDIS_STATUS_INVALID_PARAMETER; } if (machineMode == 64) { - addressWidth = ZYDIS_ADDRESS_WIDTH_64; + if (addressWidth != 64) + { + return ZYDIS_STATUS_INVALID_PARAMETER; + } } else { if ((addressWidth != 16) && (addressWidth != 32)) @@ -4352,7 +4348,7 @@ ZydisStatus ZydisDecoderInitEx(ZydisDecoder* decoder, ZydisMachineMode machineMo } if (decodeGranularity == ZYDIS_DECODE_GRANULARITY_DEFAULT) { - decodeGranularity = ZYDIS_DECODE_GRANULARITY_FULL; + decodeGranularity = ZYDIS_DECODE_GRANULARITY_SEMANTIC; } decoder->machineMode = machineMode; diff --git a/tools/PerfTest.c b/tools/PerfTest.c new file mode 100644 index 0000000..91a2478 --- /dev/null +++ b/tools/PerfTest.c @@ -0,0 +1,303 @@ +/*************************************************************************************************** + + Zyan Disassembler Engine (Zydis) + + Original Author : Florian Bernd + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + +***************************************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ZYDIS_WINDOWS +# include +#endif + +/* ============================================================================================== */ +/* Helper functions */ +/* ============================================================================================== */ + +double CounterFreq = 0.0; +uint64_t CounterStart = 0; + +void StartCounter() +{ + LARGE_INTEGER li; + if (!QueryPerformanceFrequency(&li)) + { + fputs("QueryPerformanceFrequency failed!\n", stderr); + } + CounterFreq = (double)li.QuadPart / 1000.0; + QueryPerformanceCounter(&li); + CounterStart = li.QuadPart; +} + +double GetCounter() +{ + LARGE_INTEGER li; + QueryPerformanceCounter(&li); + return (double)(li.QuadPart - CounterStart) / CounterFreq; +} + +/* ============================================================================================== */ +/* Internal functions */ +/* ============================================================================================== */ + +void processBuffer(const char* buffer, size_t length, ZydisDecodeGranularity granularity, + ZydisBool format) +{ + ZydisDecoder decoder; + if (!ZYDIS_SUCCESS(ZydisDecoderInitEx(&decoder, + ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64, granularity))) + { + fputs("Failed to initialize decoder\n", stderr); + exit(EXIT_FAILURE); + } + + ZydisFormatter formatter; + if (format) + { + if (!ZYDIS_SUCCESS(ZydisFormatterInitEx(&formatter, ZYDIS_FORMATTER_STYLE_INTEL, + ZYDIS_FMTFLAG_FORCE_SEGMENTS | ZYDIS_FMTFLAG_FORCE_OPERANDSIZE, + ZYDIS_FORMATTER_ADDR_ABSOLUTE, ZYDIS_FORMATTER_DISP_DEFAULT, + ZYDIS_FORMATTER_IMM_DEFAULT))) + { + fputs("Failed to initialized instruction-formatter\n", stderr); + exit(EXIT_FAILURE); + } + } + + size_t offset = 0; + ZydisStatus status; + ZydisDecodedInstruction instruction; + char formatBuffer[256]; + while ((status = ZydisDecoderDecodeBuffer(&decoder, buffer + offset, length - offset, offset, + &instruction)) != ZYDIS_STATUS_NO_MORE_DATA) + { + ZYDIS_ASSERT(ZYDIS_SUCCESS(status)); + if (!ZYDIS_SUCCESS(status)) + { + puts("Unexpected decoding error"); + exit(EXIT_FAILURE); + } + if (format) + { + ZydisFormatterFormatInstruction( + &formatter, &instruction, formatBuffer, sizeof(formatBuffer)); + } + offset += instruction.length; + } +} + +void testPerformance(const char* buffer, size_t length, ZydisDecodeGranularity granularity, + ZydisBool format) +{ + StartCounter(); + for (uint8_t j = 0; j < 100; ++j) + { + processBuffer(buffer, length, granularity, format); + } + printf("Granularity %d, Formatting %d: %8.2f msec\n", granularity, format, GetCounter()); +} + +void generateTestData(FILE* file, uint8_t encoding) +{ + ZydisDecoder decoder; + if (!ZYDIS_SUCCESS( + ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64))) + { + fputs("Failed to initialize decoder\n", stderr); + exit(EXIT_FAILURE); + } + + uint8_t last = 0; + double size = 0; + ZydisDecodedInstruction instruction; + while (size < 1024 * 1024) + { + uint8_t data[ZYDIS_MAX_INSTRUCTION_LENGTH]; + for (int i = 0; i < ZYDIS_MAX_INSTRUCTION_LENGTH; ++i) + { + data[i] = rand() % 256; + } + uint8_t offset = rand() % (ZYDIS_MAX_INSTRUCTION_LENGTH - 2); + switch (encoding) + { + case 0: + break; + case 1: + data[offset ] = 0x0F; + data[offset + 1] = 0x0F; + break; + case 2: + data[offset ] = 0x8F; + break; + case 3: + data[offset ] = 0xC4; + break; + case 4: + data[offset ] = 0xC5; + break; + case 5: + case 6: + data[offset ] = 0x62; + break; + default: + ZYDIS_UNREACHABLE; + } + if (ZYDIS_SUCCESS(ZydisDecoderDecodeBuffer(&decoder, data, sizeof(data), 0, &instruction))) + { + ZydisBool b = ZYDIS_FALSE; + switch (encoding) + { + case 0: + b = (instruction.encoding == ZYDIS_INSTRUCTION_ENCODING_DEFAULT); + break; + case 1: + b = (instruction.encoding == ZYDIS_INSTRUCTION_ENCODING_3DNOW); + break; + case 2: + b = (instruction.encoding == ZYDIS_INSTRUCTION_ENCODING_XOP); + break; + case 3: + case 4: + b = (instruction.encoding == ZYDIS_INSTRUCTION_ENCODING_VEX); + break; + case 5: + b = (instruction.encoding == ZYDIS_INSTRUCTION_ENCODING_EVEX); + break; + case 6: + b = (instruction.encoding == ZYDIS_INSTRUCTION_ENCODING_MVEX); + break; + default: + ZYDIS_UNREACHABLE; + } + if (b) + { + fwrite(&instruction.data[0], 1, instruction.length, file); + size += instruction.length; + + double p = (size / (1024 * 1024) * 100); + if (last < (uint8_t)p) + { + last = (uint8_t)p; + printf("%3.0f%%\n", p); + } + + } + } + } +} + +/* ============================================================================================== */ +/* Entry point */ +/* ============================================================================================== */ + +int main(int argc, char** argv) +{ + if (argc < 3 || (!strcmp(argv[1], "-test") && !strcmp(argv[1], "-generate"))) + { + fputs("Usage: PerfTest -[test|generate] [directory]", stderr); + return EXIT_FAILURE; + } + + ZydisBool generate = ZYDIS_FALSE; + if (!strcmp(argv[1], "-generate")) + { + generate = ZYDIS_TRUE; + } + const char* directory = argv[2]; + + static const struct + { + const char* encoding; + const char* filename; + } tests[7] = + { + { "DEFAULT", "enc_default.dat" }, + { "3DNOW" , "enc_3dnow.dat" }, + { "XOP" , "enc_xop.dat" }, + { "VEX_C4" , "enc_vex_c4.dat" }, + { "VEX_C5" , "enc_vex_c5.dat" }, + { "EVEX" , "enc_evex.dat" }, + { "MVEX" , "enc_mvex.dat" } + }; + + if (generate) + { + time_t t; + srand((unsigned) time(&t)); + } + + for (uint8_t i = 0; i < ZYDIS_ARRAY_SIZE(tests); ++i) + { + FILE* file; + + char buf[256]; + strcpy(&buf[0], directory); + if (generate) + { + file = fopen(strcat(buf, tests[i].filename), "wb"); + } else + { + file = fopen(strcat(buf, tests[i].filename), "rb"); + } + if (!file) + { + fprintf(stderr, "Can not open file \"%s\": %s\n", &buf[0], strerror(errno)); + continue; + } + + if (generate) + { + printf("Generating %s ...\n", tests[i].encoding); + generateTestData(file, i); + } else + { + fseek(file, 0L, SEEK_END); + long length = ftell(file); + void* buffer = malloc(length); + rewind(file); + fread(buffer, 1, length, file); + + printf("Testing %s ...\n", tests[i].encoding); + testPerformance(buffer, length, ZYDIS_DECODE_GRANULARITY_PHYSICAL, ZYDIS_FALSE); + testPerformance(buffer, length, ZYDIS_DECODE_GRANULARITY_SEMANTIC, ZYDIS_FALSE); + // testPerformance(buffer, length, ZYDIS_DECODE_GRANULARITY_SEMANTIC, ZYDIS_TRUE ); + + puts(""); + free(buffer); + fclose(file); + } + } + + getchar(); + + return 0; +} + +/* ============================================================================================== */