Assertion Errors
These are the easy ones, because the message gives you a hint. The assertion error has two lines: the first line identifies the module where the error occurred, and the second line gives you the condition that should have been true for no error to occur. The line number in the first line does not pertain to anything that we have access to, but where the same message is used several times the line number distinguishes between the uses. An assertion error occurs because the programmers knew of a dangerous condition and trapped the code for it.
Unhandled Exceptions
Where the programmers did not think of cases to test, and execution goes out of bounds, you will get a Windows error instead. The most common type of unhandled exception is ther c0000005 Access Violation error. This error happens when the code tries to access a block of memory it is not permitted to access, as regulated by the operating system itself. The most common cause of an unhandled exception is a dereferenced pointer that gets based from address 00000000 instead of the address it was supposed to receive.
Structure of A Debug Report
Debug reports can be found in your Diablo II install directory. They are named D2yymmdd.txt, where yy is the year, mm is the month and dd the day the report was generated. Any time you run D2 you generate a debug report, whether anything goes wrong or not. Under normal execution, the report contains information about your machine and what the game did when it started up and when it shut down. We will ignore these sections in this discussion.
There are several parts to a debug report that are added when an error occurs. The first section you can think of as a header, it contains the assertion or unhandled exception notice you see in the popup at error time. Below the notice there may be register listings, these are the contents of the CPU registers at the time of the error. Here are a couple of examples:
Code: Select all
15:16:58.843 -------- FILE: urce\D2Common\UNITS\UnitRoom.cpp LINE: 23 --------
Assertion Failure
Location : urce\D2Common\UNITS\UnitRoom.cpp, line #23 <--- module in error
Expression : DungeonTestRoomGame ( hRoom, nX, nY) <--- violated condition
Code: Select all
20:10:41.986 ***** UNHANDLED EXCEPTION: ACCESS_VIOLATION (c0000005)
20:10:41.986 Fault address: 6FC5B038 01:0002A038 D:\Diablo_II\D2Game.dll
20:10:41.986 eax:00000001 ebx:05c4163c ecx:00000000 edx:05c41600 esi:00000242
20:10:41.986 edi:00000000 ebp:00000000 esp:0012ecd4 eip:6fc5b038 flg:00010202
20:10:41.986 cs:001b ds:0023 es:0023 ss:0023 fs:003b gs:0000
Code: Select all
6FDAD437 |. 56 PUSH ESI
6FDAD438 |. E8 C3060000 CALL D2Common.#10326
6FDAD43D |. 56 PUSH ESI
6FDAD43E |. 8BD8 MOV EBX,EAX
6FDAD440 |. E8 2B080000 CALL D2Common.#10329
6FDAD445 |. 8B0F MOV ECX,DWORD PTR DS:[EDI]
6FDAD447 |. 3BD9 CMP EBX,ECX <--- test upper Y
6FDAD449 |. 0F8C 07010000 JL D2Common.6FDAD556 <--- fail
6FDAD44F |. 8B57 08 MOV EDX,DWORD PTR DS:[EDI+8]
6FDAD452 |. 03D1 ADD EDX,ECX
6FDAD454 |. 3BDA CMP EBX,EDX <--- test lower Y
6FDAD456 |. 0F8D FA000000 JGE D2Common.6FDAD556 <--- fail
6FDAD45C |. 8B4F 04 MOV ECX,DWORD PTR DS:[EDI+4]
6FDAD45F |. 3BC1 CMP EAX,ECX <--- test left X
6FDAD461 |. 0F8C EF00000 JL D2Common.6FDAD556 <--- fail
6FDAD467 |. 8B57 0C MOV EDX,DWORD PTR DS:[EDI+C]
6FDAD46A |. 03D1 ADD EDX,ECX
6FDAD46C |. 3BC2 CMP EAX,EDX <--- test right X
6FDAD46E |. 0F8D E2000000 JGE D2Common.6FDAD556 <--- fail
Code: Select all
6FDAD556 |> 6A 17 PUSH 17 <--- the line number you see
6FDAD558 |. 68 34E2DD6F PUSH D2Common.6FDDE234 <--- location of first string
; ASCII "C:\Src\Diablo2\Source\D2Common\UNITS\UnitRoom.cpp"
6FDAD55D |. 68 D0E1DD6F PUSH D2Common.6FDDE1D0 <--- location of second string
; ASCII "DungeonTestRoomGame ( hRoom, nX, nY)"
6FDAD562 |. E8 1B650000 CALL <JMP.&Fog.#10023> <--- sets up the popup
6FDAD567 |. 83C4 0C ADD ESP,0C
6FDAD56A |. 6A FF PUSH -1
6FDAD56C |. E8 3A660000 CALL D2Common.6FDB3BAB <--- exit call (VC++ RTL)
Below the header is the stack dump. The stack is a section of memory used to store function parameters, return addresses, buffers and local variables. Function parameters are just as they would be in a high level language. The return address is so the CPU knows where to go back to once execution encounters the RETN or RETN XX instruction. Buffers are used to build output, such as converting "%s%s.TXT" into "DATA\GLOBAL\EXCEL\ITEMTYPES.TXT". And local variables are used only within the function. The important thing to remember about stack dumps is only the first part is likely to have any relationship to the error! Here is an example:
Code: Select all
20:10:42.033 0012ECD4: 0016C405 42020000 7C00EE01 94E2C56F ..Ä.B...|.î..âÅo
0012ECE4: 0CED1200 00000000 01000000 01000000 .í..............
0012ECF4: 7C00EE01 7C00EE01 0016C405 95130000 |.î.|.î...Ä.....
0012ED04: 0000EE01 4CCBCA6F 42020000 D6100000 ..î.LËÊoB...Ö...
94E2C56F --> 6FC5E294 D2 module addresses are almost universally between 6F700000 and 70000000, so any time you see 6Fxxxxxx think "address". Now we need to know if it is code or data. From a memory map of D2 modules, you will see this is part of the memory block for d2game.dll code, so this is probably the return address for the function including the instruction at 6FC5B038. The return is always the instruction after the call that got you there, so it represents something that hasn't happened yet. But it is useful to know how you got into the function that crashed, and the return address is the only way to know where you came from if the function is called from several places.
7C00EE01 --> 01EE007C Diablo II operates on the client-server model, which is to say part of the code is acting as a game server and part as a game client. This is true even in single player. On the server side is an important dynamic structure known as ptGame, it contains data the game server uses to track all sorts of stuff. I have yet to see a ptGame that doesn't follow the pointer format 0xxx007C.
0016C405 --> 05C41600 There are other structures in D2 other than ptGame. One common structure is ptUnit, which in its various aspects contains dynamic data about characters, monsters, objects, missiles, items and tiles. These structures are aligned on one or more 256-byte memory pages, and they are assigned on the heap during gameplay, so they have the pointer format 0xxxxxx00.
xxxx0000 --> 0000xxxx Any number this small cannot be an address, that would put a pointer in forbidden territory. So these numbers are data on the stack, either as parameters or as local variables. Anything between the stack pointer and the first 6F-style address is in local variable memory. Anything above that first 6F-style address is possibly pushed parameters, buffer space, or left over from previous activity and unrelated to the error at hand.
Below the stack dump there may be a short excerpt from the function where the crash occurred, called code bytes. This is machine language, so you need to at least disassemble the code section to get a grasp of the logic. Unless you like to disassemble things by hand, you can ignore this. The only time the code bytes will be very useful is if you are debugging self-modifying code.
Next are the enumerated modules list and the loaded symbols list. All either of these do is list the executables that are visible to Diablo II when the game is running, and give you a memory map of where the DLLs ended up. But the memory map won't tell you which part is code and which part is data. You can ignore this, unless you suspect your crash occurred because a module wasn't loaded. Only mods using D2Extra or other custom DLLs are likely to show abnormal conditions in either section.
Code: Select all
20:10:42.346 Annotated stack dump
// EIP = 0x6FC5B038 - D2Game.dll - Ordinal10045+1A918
0012ECD4: 0016C405 42020000 7C00EE01 94E2C56F ..Ä.B...|.î..âÅo
// 0x6FC5E294 - D2Game.dll - Ordinal10045+1DB74
0012ECE4: 0CED1200 00000000 01000000 01000000 .í..............
0012ECF4: 7C00EE01 7C00EE01 0016C405 95130000 |.î.|.î...Ä.....
0012ED04: 0000EE01 4CCBCA6F ..î.LËÊo
// 0x6FCACB4C - D2Game.dll - Ordinal10034+1BA0C
0012ED0C: 42020000 D6100000 89130000 01000000 B...Ö...........
0012ED1C: 88130000 40FA8A03 0016C405 00000000 ....@ú....Ä.....
0012ED2C: 00F20501 06000000 80110000 88130000 .ò..............
0012ED3C: A8110000 B0130000 7C00EE01 005FC405 ¨...°...|.î.._Ä.
0012ED4C: 03000000 27000000 3C5FC405 81110000 ....'...<_Ä.....
0012ED5C: 27000000 89130000 A60DC66F '.......¦.Æo
At the end is the stack crawl section, which is a summary of information you can get from the two stack dumps.
When you are reporting an error, don't include the entire file in one massive dump on the forums. The only parts that will be meaningful will be the header (with register listing if present), and the top lines of the two stack dumps (through the first 6F-style address and another five lines at most).