Last active
December 18, 2022 02:24
-
-
Save frodo821/460ed4273c8db49aa1d2452847169496 to your computer and use it in GitHub Desktop.
Windows Hardware Error Architecture (WHEA) Raw Report Analyser
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include "whea_types.hpp" | |
const char *CPER_BIN_FILE = "CPER"; | |
const char *CPER_ASCII_FILE = "43504552"; | |
/** | |
* @brief base16エンコードされたファイルであるか、生のバイナリ形式のファイルであるかをチェックする | |
* | |
* @param fp ファイルポインタ | |
* @return int バイナリ形式なら CPER_FILE_TYPE_BINARY を返す。base16エンコード済みなら CPER_FILE_TYPE_ASCII を返す。無効なファイル形式の場合は CPER_FILE_TYPE_INVALID を返す。 | |
*/ | |
int check_file_magic(FILE *fp) | |
{ | |
char buffer[9]; | |
if (fread(buffer, 1, 8, fp) == 8) | |
{ | |
if (fseek(fp, 0L, SEEK_SET)) | |
{ | |
perror("fseek"); | |
return CPER_FILE_TYPE_INVALID; | |
} | |
buffer[8] = 0; | |
if (strcmp(buffer, CPER_ASCII_FILE) == 0) | |
{ | |
return CPER_FILE_TYPE_ASCII; | |
} | |
buffer[4] = 0; | |
if (strcmp(buffer, CPER_BIN_FILE) == 0) | |
{ | |
return CPER_FILE_TYPE_BINARY; | |
} | |
return CPER_FILE_TYPE_INVALID; | |
} | |
return CPER_FILE_TYPE_INVALID; | |
} | |
/** | |
* @brief base16文字列を2文字読み込んで1バイトに変換する | |
* | |
* @param buffer base16エンコードされた2文字(1バイト分) | |
* @return uint8_t デコードされた1バイト | |
*/ | |
uint8_t read_base16_byte(char buffer[2]) | |
{ | |
uint8_t result = 0; | |
for (int i = 0; i < 2; i++) | |
{ | |
result = result << 4; | |
if (buffer[i] >= '0' && buffer[i] <= '9') | |
{ | |
result += buffer[i] - '0'; | |
} | |
else if (buffer[i] >= 'a' && buffer[i] <= 'f') | |
{ | |
result += buffer[i] - 'a' + 10; | |
} | |
else if (buffer[i] >= 'A' && buffer[i] <= 'F') | |
{ | |
result += buffer[i] - 'A' + 10; | |
} | |
else | |
{ | |
return 0; | |
} | |
} | |
return result; | |
} | |
/** | |
* @brief base16エンコードされたファイルから、エラーレポートのヘッダを読み込む | |
* | |
* @param fp ファイルポインタ | |
* @param header ヘッダを格納するメモリ領域へのポインタ | |
* @return int ヘッダが正常に読み込まれた場合は 0 を返す。それ以外の場合は 1 を返す。 | |
*/ | |
int read_header_in_ascii(FILE *fp, whea_header_t *header) | |
{ | |
whea_header_in_t header_in; | |
char buffer[sizeof(whea_header_t) * 2]; | |
if (fread(buffer, 1, sizeof(whea_header_t) * 2, fp) != sizeof(whea_header_t) * 2) | |
{ | |
return 1; | |
} | |
for (int i = 0; i < sizeof(whea_header_t); i++) | |
{ | |
header_in.bytes[i] = read_base16_byte(&buffer[i * 2]); | |
} | |
memcpy(header, &header_in.header, sizeof(whea_header_t)); | |
return 0; | |
} | |
/** | |
* @brief base16エンコードされたファイルから、エラーレポートのセクションデスクリプタを読み込む | |
* | |
* @param fp ファイルポインタ | |
* @param section セクションデスクリプタを格納するメモリ領域へのポインタ | |
* @return int セクションデスクリプタが正常に読み込まれた場合は 0 を返す。それ以外の場合は 1 を返す。 | |
*/ | |
int read_section_in_ascii(FILE *fp, whea_error_section_descriptor_t *section) | |
{ | |
whea_error_section_descriptor_in_t section_in; | |
char buffer[sizeof(whea_error_section_descriptor_t) * 2]; | |
if (fread(buffer, 1, sizeof(whea_error_section_descriptor_t) * 2, fp) != sizeof(whea_error_section_descriptor_t) * 2) | |
{ | |
return 1; | |
} | |
for (int i = 0; i < sizeof(whea_error_section_descriptor_t); i++) | |
{ | |
section_in.bytes[i] = read_base16_byte(&buffer[i * 2]); | |
} | |
memcpy(section, §ion_in.desc, sizeof(whea_error_section_descriptor_t)); | |
return 0; | |
} | |
int main(int argc, char *argv[]) | |
{ | |
FILE *fp; | |
whea_header_t header; | |
if (argc != 2) | |
{ | |
printf("Usage: %s <file>\n", argv[0]); | |
return 1; | |
} | |
fp = fopen(argv[1], "rb"); | |
if (fp == NULL) | |
{ | |
printf("Failed to open file %s\n", argv[1]); | |
return 1; | |
} | |
size_t len; | |
int file_type = check_file_magic(fp); | |
if (file_type == CPER_FILE_TYPE_INVALID) | |
{ | |
printf("Invalid file type\n"); | |
return 1; | |
} | |
if (file_type == CPER_FILE_TYPE_ASCII) | |
{ | |
if (read_header_in_ascii(fp, &header)) | |
{ | |
printf("Failed to read header\n"); | |
return 1; | |
} | |
} | |
else | |
{ | |
if ((len = fread(&header, sizeof(char), sizeof(whea_header_t), fp)) < sizeof(whea_header_t)) | |
{ | |
printf("Failed to read header, expected %zd bytes; actual %zd bytes.\n", sizeof(whea_header_t), len); | |
fclose(fp); | |
return 1; | |
} | |
} | |
printf("Signature +%04x: %.4s\n", offsets_h(Signature), header.Signature.AsCHAR); | |
printf("Revision +%04x: %u.%u (%u)\n", offsets_h(Revision), header.Revision.MajorRevision, header.Revision.MinorRevision, header.Revision.AsUSHORT); | |
printf("SignatureEnd +%04x: %lx\n", offsets_h(SignatureEnd), header.SignatureEnd); | |
printf("SectionCount +%04x: %lu\n", offsets_h(SectionCount), header.SectionCount); | |
printf("ErrorSeverity +%04x: %u\n", offsets_h(Severity), header.Severity); | |
printf("ValidationBits +%04x: %lu\n", offsets_h(ValidBits), header.ValidBits.AsULONG); | |
printf("Length +%04x: %lu\n", offsets_h(Length), header.Length); | |
printf("Timestamp +%04x: 20%02lld-%02lld-%02lld %02lld:%02lld:%02lld\n", | |
offsets_h(Timestamp), | |
header.Timestamp.Year, | |
header.Timestamp.Month, | |
header.Timestamp.Day, | |
header.Timestamp.Hours, | |
header.Timestamp.Minutes, | |
header.Timestamp.Seconds); | |
printf("PlatformId +%04x: " format_guid "\n", offsets_h(PlatformId), format_guid_args(header.PlatformId)); | |
printf("PartitionId +%04x: " format_guid "\n", offsets_h(PartitionId), format_guid_args(header.PartitionId)); | |
printf("CreatorId +%04x: " format_guid "\n", offsets_h(CreatorId), format_guid_args(header.CreatorId)); | |
printf("NotifyType +%04x: " format_guid "\n", offsets_h(NotifyType), format_guid_args(header.NotifyType)); | |
printf("RecordId +%04x: %llu\n", offsets_h(RecordId), header.RecordId); | |
printf("Flags +%04x: %04lx\n", offsets_h(Flags), header.Flags.AsULONG); | |
printf("PersistenceInfo +%04x: %08llx\n", offsets_h(PersistenceInfo), header.PersistenceInfo.AsULONGLONG); | |
printf("\n===============================\n"); | |
whea_error_section_descriptor_t descriptor; | |
for (int i = 0; i < header.SectionCount; i++) | |
{ | |
if (file_type == CPER_FILE_TYPE_ASCII) | |
{ | |
if (read_section_in_ascii(fp, &descriptor)) | |
{ | |
printf("Failed to read section descriptor\n"); | |
return 1; | |
} | |
} | |
else | |
{ | |
if ((len = fread(&descriptor, sizeof(char), sizeof(whea_error_section_descriptor_t), fp)) < sizeof(whea_error_section_descriptor_t)) | |
{ | |
printf("Failed to read descriptor, expected %zd bytes; actual %zd bytes.\n", sizeof(whea_error_section_descriptor_t), len); | |
fclose(fp); | |
return 1; | |
} | |
} | |
printf("---> Section %d\n", i); | |
printf("SectionOffset +%04x: %lu\n", offsets_s(SectionOffset, i), descriptor.SectionOffset); | |
printf("SectionLength +%04x: %lu\n", offsets_s(SectionLength, i), descriptor.SectionLength); | |
printf("Revision +%04x: %u.%u (%u)\n", offsets_s(Revision, i), descriptor.Revision.MajorRevision, descriptor.Revision.MinorRevision, descriptor.Revision.AsUSHORT); | |
printf("ValidationBits +%04x: %u\n", offsets_s(ValidBits, i), descriptor.ValidBits.AsUCHAR); | |
printf("Flags +%04x: %lu\n", offsets_s(Flags, i), descriptor.Flags.AsULONG); | |
printf("SectionType +%04x: " format_guid "\n", offsets_s(SectionType, i), format_guid_args(descriptor.SectionType)); | |
printf("FRUId +%04x: " format_guid "\n", offsets_s(FRUId, i), format_guid_args(descriptor.FRUId)); | |
printf("SectionSeverity +%04x: %u\n", offsets_s(SectionSeverity, i), descriptor.SectionSeverity); | |
printf("FRUText +%04x: '%.20s'\n\n", offsets_s(FRUText, i), descriptor.FRUText); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef CPER_TYPES_HPP | |
#define CPER_TYPES_HPP | |
/** | |
* @brief 構造体のメンバのオフセットを計算する | |
*/ | |
#define offsetof(s, m) ((size_t) & (((s *)0)->m)) | |
/** | |
* @brief whea_header_t のメンバのオフセットを計算する | |
*/ | |
#define offsets_h(x) ((unsigned)offsetof(whea_header_t, x)) | |
/** | |
* @brief whea_error_section_descriptor_t のメンバのファイルの先頭からのオフセットを計算する | |
*/ | |
#define offsets_s(x, i) (unsigned)(offsetof(whea_error_section_descriptor_t, x) + sizeof(whea_error_section_descriptor_t) * (i) + sizeof(whea_header_t)) | |
/** | |
* @brief バイナリ形式のレポートファイルを表す | |
*/ | |
#define CPER_FILE_TYPE_BINARY 0 | |
/** | |
* @brief base16エンコードされたレポートファイルを表す | |
*/ | |
#define CPER_FILE_TYPE_ASCII 1 | |
/** | |
* @brief 無効な形式のレポートファイルを表す | |
*/ | |
#define CPER_FILE_TYPE_INVALID -1 | |
#define _h2_bytes_of_last_8_bytes(data) ((((uint16_t)data[0]) << 8) | (((uint16_t)data[1]))) | |
#define _l6_bytes_of_last_8_bytes(data) ((((uint64_t)data[2]) << 40) | (((uint64_t)data[3]) << 32) | \ | |
(((uint64_t)data[4]) << 24) | (((uint64_t)data[5]) << 16) | \ | |
(((uint64_t)data[6]) << 8) | (((uint64_t)data[7]))) | |
#define format_guid "%08x-%04x-%04x-%04x-%012llx" | |
#define format_guid_args(guid) (guid).data1, \ | |
(guid).data2, \ | |
(guid).data3, \ | |
_h2_bytes_of_last_8_bytes((guid).data4), \ | |
_l6_bytes_of_last_8_bytes((guid).data4) | |
#pragma pack(1) | |
/** | |
* とりあえず使いそうな整数型をサイズ別に定義しておく。 | |
* 本来的には stdint.h を使うべきだが、cl.exe でビルドしようとしたときにエラーが出たので一応念のため。 | |
*/ | |
typedef signed char int8_t; | |
typedef short int16_t; | |
typedef int int32_t; | |
typedef long long int64_t; | |
typedef unsigned char uint8_t; | |
typedef unsigned short uint16_t; | |
typedef unsigned int uint32_t; | |
typedef unsigned long long uint64_t; | |
typedef struct | |
{ | |
uint32_t data1; | |
uint16_t data2; | |
uint16_t data3; | |
uint8_t data4[8]; | |
} guid_t; | |
typedef union | |
{ | |
struct | |
{ | |
uint32_t PlatformId : 1; | |
uint32_t Timestamp : 1; | |
uint32_t PartitionId : 1; | |
uint32_t Reserved : 29; | |
}; | |
uint32_t AsULONG; | |
} whea_erh_valid_bits_t; | |
typedef union | |
{ | |
struct | |
{ | |
uint8_t MinorRevision; | |
uint8_t MajorRevision; | |
}; | |
uint16_t AsUSHORT; | |
} whea_revision_t; | |
typedef enum | |
{ | |
WheaErrSevRecoverable = 0, | |
WheaErrSevFatal = 1, | |
WheaErrSevCorrected = 2, | |
WheaErrSevInformational = 3 | |
} whea_error_severity_t; | |
typedef union | |
{ | |
struct | |
{ | |
uint64_t Seconds : 8; | |
uint64_t Minutes : 8; | |
uint64_t Hours : 8; | |
uint64_t Precise : 1; | |
uint64_t Reserved : 7; | |
uint64_t Day : 8; | |
uint64_t Month : 8; | |
uint64_t Year : 8; | |
uint64_t Century : 8; | |
}; | |
uint64_t AsULONGLONG; | |
} whea_timestamp_t; | |
typedef union | |
{ | |
struct | |
{ | |
uint64_t Signature : 16; | |
uint64_t Length : 24; | |
uint64_t Identifier : 16; | |
uint64_t Attributes : 2; | |
uint64_t DoNotLog : 1; | |
uint64_t Reserved : 5; | |
}; | |
uint64_t AsULONGLONG; | |
} whea_persistence_info_t; | |
typedef union | |
{ | |
struct | |
{ | |
uint32_t Recovered : 1; | |
uint32_t PreviousError : 1; | |
uint32_t Simulated : 1; | |
uint32_t DeviceDriver : 1; | |
uint32_t CriticalEvent : 1; | |
uint32_t PersistPfn : 1; | |
uint32_t SectionsTruncated : 1; | |
uint32_t RecoveryInProgress : 1; | |
uint32_t Throttle : 1; | |
uint32_t Reserved : 23; | |
}; | |
uint32_t AsULONG; | |
} whea_erh_flags_t; | |
typedef struct | |
{ | |
union | |
{ | |
uint32_t AsLONG; | |
char AsCHAR[4]; | |
} Signature; | |
whea_revision_t Revision; | |
uint32_t SignatureEnd; | |
uint16_t SectionCount; | |
whea_error_severity_t Severity; | |
whea_erh_valid_bits_t ValidBits; | |
uint32_t Length; | |
whea_timestamp_t Timestamp; | |
guid_t PlatformId; | |
guid_t PartitionId; | |
guid_t CreatorId; | |
guid_t NotifyType; | |
uint64_t RecordId; | |
whea_erh_flags_t Flags; | |
whea_persistence_info_t PersistenceInfo; | |
union | |
{ | |
struct | |
{ | |
uint32_t OsBuildNumber; | |
uint8_t Reserved2[8]; | |
}; | |
uint8_t Reserved[12]; | |
}; | |
} whea_header_t; | |
typedef union | |
{ | |
struct | |
{ | |
uint8_t FRUId : 1; | |
uint8_t FRUText : 1; | |
uint8_t Reserved : 6; | |
}; | |
uint8_t AsUCHAR; | |
} whea_ersd_valid_bits_t; | |
typedef union | |
{ | |
struct | |
{ | |
uint32_t Primary : 1; | |
uint32_t ContainmentWarning : 1; | |
uint32_t Reset : 1; | |
uint32_t ThresholdExceeded : 1; | |
uint32_t ResourceNotAvailable : 1; | |
uint32_t LatentError : 1; | |
uint32_t Propagated : 1; | |
uint32_t FruTextByPlugin : 1; | |
uint32_t Reserved : 24; | |
}; | |
uint32_t AsULONG; | |
} whea_ersd_flags_t; | |
typedef struct | |
{ | |
uint32_t SectionOffset; | |
uint32_t SectionLength; | |
whea_revision_t Revision; | |
whea_ersd_valid_bits_t ValidBits; | |
uint8_t Reserved; | |
whea_ersd_flags_t Flags; | |
guid_t SectionType; | |
guid_t FRUId; | |
whea_error_severity_t SectionSeverity; | |
char FRUText[20]; | |
} whea_error_section_descriptor_t; | |
typedef union | |
{ | |
whea_header_t header; | |
uint8_t bytes[sizeof(whea_header_t)]; | |
} whea_header_in_t; | |
typedef union | |
{ | |
whea_error_section_descriptor_t desc; | |
uint8_t bytes[sizeof(whea_error_section_descriptor_t)]; | |
} whea_error_section_descriptor_in_t; | |
#pragma pack() | |
#endif |
GUIDの定義を含めた。これによって Windows.h
への依存を消せたため、どの環境の gcc
でも大抵コンパイル可能なコードとなった。
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Windowsイベントビューアで見ることができる、BASE16エンコードされた形式のレポートをそのまま読み込めるように変更した