i have a TMemory stream that is filled from a proccess, i need to read an other part of it real time.when i use this code :
for i := 0 to j do
begin
FOutputStream.position:=i * 194
stream4.CopyFrom(FOutputStream, 194 );
end;
it return wrong data because the writer process change the position.
so i decided to use Memory property
stream4.CopyFrom( PByte(FOutputStream.Memory)[ i * 194 ] , 194) );
but i got this error
[DCC Error] Unit1.pas(640): E2010 Incompatible types: 'TStream' and
'Byte'
how can i handle this error?
You cannot use CopyFrom directly in this case, because that requires a stream, and you have a pointer.
You could solve this by creating a stream object that wrapped the memory owned by another memory stream. However that is needlessly complex. You merely need to call WriteBuffer.
stream4.WriteBuffer(PByte(FOutputStream.Memory)[i * 194] , 194);
I presume that you know this, but since you are operating from different threads when reading from and writing to the memory stream, you need to make sure that these actions account for any potential thread safety issues.
Related
In WIN32:
I'm sure that if the handle is the same, the memory may not be the same, and the same handle will be returned no matter how many times getMemoryWin32HandleKHR is executed.
This is consistent with vulkan's official explanation: Vulkan shares memory.
It doesn't seem to work properly in Linux.
In my program,
getMemoryWin32HandleKHR works normally and can return a different handle for each different memory.
The same memory returns the same handle.
But in getMemoryFdKHR, different memories return the same fd.
Or the same memory executes getMemoryFdKHR twice, it can return two different handles.
This causes me to fail the device memory allocation during subsequent imports.
I don't understand why this is?
Thanks!
#ifdef WIN32
texGl.handle = device.getMemoryWin32HandleKHR({ info.memory, vk::ExternalMemoryHandleTypeFlagBits::eOpaqueWin32 });
#else
VkDeviceMemory memory=VkDeviceMemory(info.memory);
int file_descriptor=-1;
VkMemoryGetFdInfoKHR get_fd_info{
VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR, nullptr, memory,
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT
};
VkResult result= vkGetMemoryFdKHR(device,&get_fd_info,&file_descriptor);
assert(result==VK_SUCCESS);
texGl.handle=file_descriptor;
// texGl.handle = device.getMemoryFdKHR({ info.memory, vk::ExternalMemoryHandleTypeFlagBits::eOpaqueFd });
Win32 is nomal.
Linux is bad.
It will return VK_ERROR_OUT_OF_DEVICE_MEMORY.
#ifdef _WIN32
VkImportMemoryWin32HandleInfoKHR import_allocate_info{
VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR, nullptr,
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT, sharedHandle, nullptr };
#elif __linux__
VkImportMemoryFdInfoKHR import_allocate_info{
VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR, nullptr,
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT,
sharedHandle};
#endif
VkMemoryAllocateInfo allocate_info{
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // sType
&import_allocate_info, // pNext
aligned_data_size_, // allocationSize
memory_index };
VkDeviceMemory device_memory=VK_NULL_HANDLE;
VkResult result = vkAllocateMemory(m_device, &allocate_info, nullptr, &device_memory);
NVVK_CHECK(result);
I think it has something to do with fd.
In my some test: if I try to get fd twice. use the next fd that vkAllocateMemory is work current......but I think is error .
The fd obtained in this way is different from the previous one.
Because each acquisition will be a different fd.
This makes it impossible for me to distinguish, and the following fd does vkAllocateMemory.
Still get an error.
So this test cannot be used.
I still think it should have the same process as win32. When the fd is obtained for the first time, vkAllocateMemory can be performed correctly.
thanks very much!
The Vulkan specifications for the Win32 handle and POSIX file descriptor interfaces explicitly state different things about their importing behavior.
For HANDLEs:
Importing memory object payloads from Windows handles does not transfer ownership of the handle to the Vulkan implementation. For handle types defined as NT handles, the application must release handle ownership using the CloseHandle system call when the handle is no longer needed.
For FDs:
Importing memory from a file descriptor transfers ownership of the file descriptor from the application to the Vulkan implementation. The application must not perform any operations on the file descriptor after a successful import.
So HANDLE importation leaves the HANLDE in a valid state, still referencing the memory object. File descriptor importation claims ownership of the FD, leaving it in a place where you cannot use it.
What this means is that the FD may have been released by the internal implementation. If that is the case, later calls to create a new FD may use the same FD index as a previous call.
The safest way to use both of these APIs is to have the Win32 version emulate the functionality of the FD version. Don't try to do any kinds of comparisons of handles. If you need some kind of comparison logic, then you'll have to implement it yourself. When you import a HANDLE, close it immediately afterwards.
This is Delphi 10.1 Berlin (probably unpatched).
This code is pretty basic.
This code is not stable.
Why?
procedure TBLEEnumerator.WriteToSubScribedCharacteristic(bs: TArray<byte>);
var
//member ble is an instance of TBluetoothLE
chr: TBluetoothGattCharacteristic;
begin
chr := ble.GetCharacteristic(FBLEGattService, ChooseCharacteristic);
chr.SetValue(bs);
ble.WriteCharacteristic(self.dev, chr);
end;
A few other notes:
This error is when running on Windows, but I intend this code to run also on mobile devices.
This function is called from the Main Thread and is a wrapper around the TBluetoothLE component. The original connection/scan is also handled from the main thread.
It is pretty simple, obviously. TArray is a reference-counted dynamic array.
Here's the call stack when it tanks:
:761d31ce ucrtbase.memcpy + 0x4e
System.Win.BluetoothWinRT.BytesToIBuffer(???,???)
System.Win.BluetoothWinRT.TWinRTBluetoothGattCharacteristic.SetValueToDevice
System.Win.BluetoothWinRT.TWinRTBluetoothLEDevice.DoWriteCharacteristic$52$ActRec.$0$Body
System.Classes.TAnonymousThread.Execute
System.Classes.ThreadProc($7511F40)
System.ThreadWrapper($5A30D30)
:740162c4 KERNEL32.BaseThreadInitThunk + 0x24
:77060fd9 ;
:77060fa4 ;
Here's the deal. This is a BUG in the Embarcadero Libraries. Yes, Yet another bug that they will probably never fix.
Here's how to fix it for yourself:
copy System.Win.BluetoothWinRT.pas to your project directory or some other path used by the project.
open the file in the IDE.
Search for "BytestoIBuffer".
Remove the word "const" from the function signature.
Very few people seem to realize this (including the authors of FMX and VCL) but the use of "const" strings and dynamic arrays causes the Win32 compiler to optimize-out the reference count handling for those structures. I don't know if it affects the mobile compilers or the Win64 compiler, but maybe.
The nasty effect of this is that if that string or dynamic array originated from another thread, that other thread might very well decide to destroy that string or dynamic array before the new thread (in this case an anonymous thread created to write-behind the data) is finished working with it.
Removing const from this function fixed the instability.
I am developing this Delhi 2009 app with SQLite database version 3.7.9, accessed by the Zeos components. Started off OK - I can create a DB if none exists, create tables and insert records - the 'normal stuff'. There is a little table that contains serial params for a connection to some old equipments and the serial comms handler needs to be informed of connection parameter changes, but the Zeos components do not support events on SQLite.
I looked at the SQLite API and there is a 'Data change notification callback', so I set about making this happen. The setup prototype is:
void *sqlite3_update_hook(
sqlite3*,
void(*)(void *,int ,char const *,char const *,sqlite3_int64),
void*
);
So, in Delphi, I imported the DLL call:
function sqlite3_update_hook(pDB:pointer;pCallback:pointer;aux:pointer): pointer; stdcall;
..
function sqlite3_update_hook; external 'sqlite3.dll' name 'sqlite3_update_hook';
..and declared an empty callback for testing:
function DBcallback(aux:pointer;CBtype:integer;CBdatabaseName:PChar;CBtableName:PChar;CBrowID:Int64):pointer; stdcall;
begin
end;
..and called the setup:
sqlite3_update_hook(SQLiteHandle,#DBcallback,dmOilmon);
SQLiteHandle is the sqlite3* pointer for the database as retrieved from the driver after connection. dmOilmon is the DataModule instance so that, when working correctly, I can call some method from the callback, (yes, I know callback is in an unknown thread - I can deal with that OK, I'll just be signaling semaphores).
Good news: when I ran the app, the DBcallback was called, aparrently successfully, upon the first insert. Bad news: some little time later, the app blew up with 'Too many exceptions', and usually a CPU window full of '??', (the occasional alternative was system death and a reboot - Vista Ultimate 64). Breaking in the callback, aux, CBtype and CBrowID were all as expected but the debugger tooltip showed the CBdatabaseName / CBtableName PChars to be pointing at a string of Chinese characters..
I tried to trace where the callback is called from - the call chain passes through in the Zeos driver code, for some reason. Breaking there and stepping through, I checked the stack pointer and it's the same before and after the callback.
So, I set up the 'empty' callback, the callback gets called at the expected point but with a coupe of dodgy-looking parameters, the callback returns to whence it came with the SP correct. I seem to have done everything right, but... :((
Has anyone seen this, (or fixed it even:)?
Can you suggest a mechanism whereby an aparrently successful callback can pesuade an app to later generate recursive exceptions?
Does anyone have any suggestions for further debugging?
Rgds,
Martin
You have the problems with correct translation of SQLite header to Delphi and with linking:
Not stdcall, but must be cdecl.
Your function DBcallback must be a procedure. Because void(*)(...) is a pointer to C function returning void.
Not PChar, but must be PAnsiChar. For non-Unicode Delphi's it may be not important, for Unicode Delphi's, it is only a correct translation.
You are using compile-time dynamic linking (external 'sqlite3.dll' ...) to sqlite3.dll. But ZeosLib uses run-time dynamic linking (LoadLibrary & GetProcAddress). That may lead to "DLL hell" issue, when your EXE loads one sqlite3.dll, and ZeosLib plans to load another sqlite3.dll.
Right-o, I'm working on implementing DirectSound in our Delphi voip app (the app allows for radios to be used by multiple users over a network connection)
Data comes in via UDP broadcasts.
As it is now, we go down on a raw data level and do the audio mixing from multiple sources ourselves and have a centralized component that is used to play all this back.
The app itself is a Delphi 5 app and I'm tasked with porting it to Delphi 2010. Once I got to this audio playback part, we concluded that it is best if we can get rid of this old code and replace it with directsound.
So, the idea is to have one SecondaryBuffer per radio (we have one 'panel' each per radio connection, based on a set of components that we create for every specific radio) and just let these add data to their respective SecondaryBuffers whenever they get data, only pausing to fill up half a second worth of audio data in the buffer if it runs out of data.
Now, I'm stuck at the part where I'm adding data to the buffers in my test-application where I'm just trying to get this to work properly before I start writing a component to utilize it the way we want.
I'm using the ported DirectX headers for Delphi (http://www.clootie.ru/delphi/download_dx92.html)
The point of these headers is to port over the regular DirectSound interface to Delphi, so hopefully non-Delphi programmers with DirectSound may know what the cause of my problem is as well.
My SecondaryBuffer (IDirectSoundBuffer) was created as follows:
var
BufferDesc: DSBUFFERDESC;
wfx: tWAVEFORMATEX;
wfx.wFormatTag := WAVE_FORMAT_PCM;
wfx.nChannels := 1;
wfx.nSamplesPerSec := 8000;
wfx.wBitsPerSample := 16;
wfx.nBlockAlign := 2; // Channels * (BitsPerSample/2)
wfx.nAvgBytesPerSec := 8000 * 2; // SamplesPerSec * BlockAlign
BufferDesc.dwSize := SizeOf(DSBUFFERDESC);
BufferDesc.dwFlags := (DSBCAPS_GLOBALFOCUS or DSBCAPS_GETCURRENTPOSITION2 or DSBCAPS_CTRLPOSITIONNOTIFY);
BufferDesc.dwBufferBytes := wfx.nAvgBytesPerSec * 4; //Which should land at 64000
BufferDesc.lpwfxFormat := #wfx;
case DSInterface.CreateSoundBuffer(BufferDesc, DSCurrentBuffer, nil) of
DS_OK: ;
DSERR_BADFORMAT: ShowMessage('DSERR_BADFORMAT');
DSERR_INVALIDPARAM: ShowMessage('DSERR_INVALIDPARAM');
end;
I left out the parts where I defined my PrimaryBuffer (it's set to play with the looping flag and was created exactly as MSDN says it should be) and the DSInterface, but it is as you might imagine an IDirectSoundInterface.
Now, every time I get an audio message (detected, decoded and converted to the appropriate audio format by other components we have made that have been confirmed to work for over seven years), I do the following:
DSCurrentBuffer.Lock(0, 512, #FirstPart, #FirstLength, #SecondPart, #SecondLength, DSBLOCK_FROMWRITECURSOR);
Move(AudioData, FirstPart^, FirstLength);
if SecondLength > 0 then
Move(AudioData[FirstLength], SecondPart^, SecondLength);
DSCurrentBuffer.GetStatus(Status);
DSCurrentBuffer.GetCurrentPosition(#PlayCursorPosition, #WriteCursorPosition);
if (FirstPart <> nil) or (SecondPart <> nil) then
begin
Memo1.Lines.Add('FirstLength = ' + IntToStr(FirstLength));
Memo1.Lines.Add('PlayCursorPosition = ' + IntToStr(PlayCursorPosition));
Memo1.Lines.Add('WriteCursorPosition = ' + IntToStr(WriteCursorPosition));
end;
DSCurrentBuffer.Unlock(#FirstPart, FirstLength, #SecondPart, SecondLength);
AudioData contains the data in my message. Messages always contain 512 bytes of audio data.
I added the Memo1.Lines.Add lines to be able to get some debug output (since using breakpoints doesn't quite work, as directsound keeps playing the contents of the primary buffer regardless)
Now, when I'm playing my DSCurrentBuffer using the looping flag (which according to hte MSDN docs is enough to make it a Streaming Buffer) and having this code work out as it wants, my output text in the Memo show that I am being allowed to write up until the end of the buffer... But it doesn't wrap.
SecondPart is always nil. It never ever wraps around to the beginning of the buffer, which means I get the same few seconds of audio data playing over and over.
And yes, I have scoured the net for components that can do this stuff for us and have concluded that the only reliable way is to do it ourselves like this.
And yes, the audio data that this app plays is choppy. I'm holding off on writing the half-a-second buffering code until I can get the write-to-buffer code to wrap as it should :/
I have been reading that people suggest keeping track of your own write cursor, but from what I read Lock and Unlock should help me bypass that need.
I'd also rather avoid having to have two buffers that I alternate between back and forth (or a split-buffer, which would essentially be the same thing, only a bit more complex in writing)
Any help greatly appreciated!
So I figured out the problem ^^;
Pretty simple too.
DSCurrentBuffer.Unlock(#FirstPart, FirstLength, #SecondPart, SecondLength);
I thought I was supposed to just pass along the same pointers to Pointers that Lock() had required.
Changing it to
DSCurrentBuffer.Unlock(FirstPart, FirstLength, SecondPart, SecondLength);
Solved the issue and the buffer now wraps correctly.
Sorry for wasting your time, but thanks anyway ^^;
A few things that might cause this:
Memo1.Lines.Add should only be called from the main thread (the thread that initialized the VCL GUI). Use TThread.Synchronize for this (easier), or an intermediate buffer that is thread safe and preferably lock-free (faster; thanks mghie for this hint).
Unlock should be in a finally section like below, because if an exception gets raised, you never unlock the buffer, see code sample below.
You should log any exceptions taking place.
Sample code:
DSCurrentBuffer.Lock(0, 512, #FirstPart, #FirstLength, #SecondPart, #SecondLength, DSBLOCK_FROMWRITECURSOR);
try
//...
finally
DSCurrentBuffer.Unlock(#FirstPart, FirstLength, #SecondPart, SecondLength);
end;
--jeroen
I'm a member in a team that use Delphi 2007 for a larger application and we suspect heap corruption because sometimes there are strange bugs that have no other explanation.
I believe that the Rangechecking option for the compiler is only for arrays. I want a tool that give an exception or log when there is a write on a memory address that is not allocated by the application.
Regards
EDIT: The error is of type:
Error: Access violation at address 00404E78 in module 'BoatLogisticsAMCAttracsServer.exe'. Read of address FFFFFFDD
EDIT2: Thanks for all suggestions. Unfortunately I think that the solution is deeper than that. We use a patched version of Bold for Delphi as we own the source. Probably there are some errors introduced in the Bold framwork. Yes we have a log with callstacks that are handled by JCL and also trace messages. So a callstack with the exception can lock like this:
20091210 16:02:29 (2356) [EXCEPTION] Raised EBold: Failed to derive ServerSession.mayDropSession: Boolean
OCL expression: not active and not idle and timeout and (ApplicationKernel.allinstances->first.CurrentSession <> self)
Error: Access violation at address 00404E78 in module 'BoatLogisticsAMCAttracsServer.exe'. Read of address FFFFFFDD. At Location BoldSystem.TBoldMember.CalculateDerivedMemberWithExpression (BoldSystem.pas:4016)
Inner Exception Raised EBold: Failed to derive ServerSession.mayDropSession: Boolean
OCL expression: not active and not idle and timeout and (ApplicationKernel.allinstances->first.CurrentSession <> self)
Error: Access violation at address 00404E78 in module 'BoatLogisticsAMCAttracsServer.exe'. Read of address FFFFFFDD. At Location BoldSystem.TBoldMember.CalculateDerivedMemberWithExpression (BoldSystem.pas:4016)
Inner Exception Call Stack:
[00] System.TObject.InheritsFrom (sys\system.pas:9237)
Call Stack:
[00] BoldSystem.TBoldMember.CalculateDerivedMemberWithExpression (BoldSystem.pas:4016)
[01] BoldSystem.TBoldMember.DeriveMember (BoldSystem.pas:3846)
[02] BoldSystem.TBoldMemberDeriver.DoDeriveAndSubscribe (BoldSystem.pas:7491)
[03] BoldDeriver.TBoldAbstractDeriver.DeriveAndSubscribe (BoldDeriver.pas:180)
[04] BoldDeriver.TBoldAbstractDeriver.SetDeriverState (BoldDeriver.pas:262)
[05] BoldDeriver.TBoldAbstractDeriver.Derive (BoldDeriver.pas:117)
[06] BoldDeriver.TBoldAbstractDeriver.EnsureCurrent (BoldDeriver.pas:196)
[07] BoldSystem.TBoldMember.EnsureContentsCurrent (BoldSystem.pas:4245)
[08] BoldSystem.TBoldAttribute.EnsureNotNull (BoldSystem.pas:4813)
[09] BoldAttributes.TBABoolean.GetAsBoolean (BoldAttributes.pas:3069)
[10] BusinessClasses.TLogonSession._GetMayDropSession (code\BusinessClasses.pas:31854)
[11] DMAttracsTimers.TAttracsTimerDataModule.RemoveDanglingLogonSessions (code\DMAttracsTimers.pas:237)
[12] DMAttracsTimers.TAttracsTimerDataModule.UpdateServerTimeOnTimerTrig (code\DMAttracsTimers.pas:482)
[13] DMAttracsTimers.TAttracsTimerDataModule.TimerKernelWork (code\DMAttracsTimers.pas:551)
[14] DMAttracsTimers.TAttracsTimerDataModule.AttracsTimerTimer (code\DMAttracsTimers.pas:600)
[15] ExtCtrls.TTimer.Timer (ExtCtrls.pas:2281)
[16] Classes.StdWndProc (common\Classes.pas:11583)
The inner exception part is the callstack at the moment an exception is reraised.
EDIT3: The theory right now is that the Virtual Memory Table (VMT) is somehow broken. When this happen there is no indication of it. Only when a method is called an exception is raised (ALWAYS on address FFFFFFDD, -35 decimal) but then it is too late. You don't know the real cause for the error. Any hint of how to catch a bug like this is really appreciated!!! We have tried with SafeMM, but the problem is that the memory consumption is too high even when the 3 GB flag is used. So now I try to give a bounty to the SO community :)
EDIT4: One hint is that according the log there is often (or even always) another exception before this. It can be for example optimistic locking in the database. We have tried to raise exceptions by force but in test environment it just works fine.
EDIT5: Story continues... I did a search on the logs for the last 30 days now. The result:
"Read of address FFFFFFDB" 0
"Read of address FFFFFFDC" 24
"Read of address FFFFFFDD" 270
"Read of address FFFFFFDE" 22
"Read of address FFFFFFDF" 7
"Read of address FFFFFFE0" 20
"Read of address FFFFFFE1" 0
So the current theory is that an enum (there is a lots in Bold) overwrite a pointer. I got 5 hits with different address above. It could mean that the enum holds 5 values where the second one is most used. If there is an exception a rollback should occur for the database and Boldobjects should be destroyed. Maybe there is a chance that not everything is destroyed and a enum still can write to an address location. If this is true maybe it is possible to search the code by a regexpr for an enum with 5 values ?
EDIT6: To summarize, no there is no solution to the problem yet. I realize that I may mislead you a bit with the callstack. Yes there are a timer in that but there are other callstacks without a timer. Sorry for that. But there are 2 common factors.
An exception with Read of address FFFFFFxx.
Top of callstack is System.TObject.InheritsFrom (sys\system.pas:9237)
This convince me that VilleK best describe the problem.
I'm also convinced that the problem is somewhere in the Bold framework.
But the BIG question is, how can problems like this be solved ?
It is not enough to have an Assert like VilleK suggest as the damage has already happened and the callstack is gone at that moment. So to describe my view of what may cause the error:
Somewhere a pointer is assigned a bad value 1, but it can be also 0, 2, 3 etc.
An object is assigned to that pointer.
There is method call in the objects baseclass. This cause method TObject.InheritsForm to be called and an exception appear on address FFFFFFDD.
Those 3 events can be together in the code but they may also be used much later. I think this is true for the last method call.
EDIT7: We work closely with the the author of Bold Jan Norden and he recently found a bug in the OCL-evaluator in Bold framework. When this was fixed these kinds of exceptions decreased a lot but they still occasionally come. But it is a big relief that this is almost solved.
You write that you want there to be an exception if
there is a write on a memory address that is not allocated by the application
but that happens anyway, both the hardware and the OS make sure of that.
If you mean you want to check for invalid memory writes in your application's allocated address range, then there is only so much you can do. You should use FastMM4, and use it with its most verbose and paranoid settings in debug mode of your application. This will catch a lot of invalid writes, accesses to already released memory and such, but it can't catch everything. Consider a dangling pointer that points to another writeable memory location (like the middle of a large string or array of float values) - writing to it will succeed, and it will trash other data, but there's no way for the memory manager to catch such access.
I don't have a solution but there are some clues about that particular error message.
System.TObject.InheritsFrom subtracts the constant vmtParent from the Self-pointer (the class) to get pointer to the adress of the parent class.
In Delphi 2007 vmtParent is defined:
vmtParent = -36;
So the error $FFFFFFDD (-35) sounds like the class pointer is 1 in this case.
Here is a test case to reproduce it:
procedure TForm1.FormCreate(Sender: TObject);
var
I : integer;
O : tobject;
begin
I := 1;
O := #I;
O.InheritsFrom(TObject);
end;
I've tried it in Delphi 2010 and get 'Read of address FFFFFFD1' because the vmtParent is different between Delphi versions.
The problem is that this happens deep inside the Bold framework so you may have trouble guarding against it in your application code.
You can try this on your objects that are used in the DMAttracsTimers-code (which I assume is your application code):
Assert(Integer(Obj.ClassType)<>1,'Corrupt vmt');
It sounds like you have memory corruption of object instance data.
The VMT itself isn't getting corrupted, FWIW: the VMT is (normally) stored in the executable and the pages that map to it are read-only. Rather, as VilleK says, it looks like the first field of the instance data in your case got overwritten with a 32-bit integer with value 1. This is easy enough to verify: check the instance data of the object whose method call failed, and verify that the first dword is 00000001.
If it is indeed the VMT pointer in the instance data that is being corrupted, here's how I'd find the code that corrupts it:
Make sure there is an automated way to reproduce the issue that doesn't require user input. The issue may be only reproducible on a single machine without reboots between reproductions owing to how Windows may choose to lay out memory.
Reproduce the issue and note the address of the instance data whose memory is corrupted.
Rerun and check the second reproduction: make sure that the address of the instance data that was corrupted in the second run is the same as the address from the first run.
Now, step into a third run, put a 4-byte data breakpoint on the section of memory indicated by the previous two runs. The point is to break on every modification to this memory. At least one break should be the TObject.InitInstance call which fills in the VMT pointer; there may be others related to instance construction, such as in the memory allocator; and in the worst case, the relevant instance data may have been recycled memory from previous instances. To cut down on the amount of stepping needed, make the data breakpoint log the call stack, but not actually break. By checking the call stacks after the virtual call fails, you should be able to find the bad write.
mghie is right of course. (fastmm4 calls the flag fulldebugmode or something like that).
Note that that works usually with barriers just before and after an heap allocation that are regularly checked (on every heapmgr access?).
This has two consequences:
the place where fastmm detects the error might deviate from the spot where it happens
a total random write (not overflow of existing allocation) might not be detected.
So here are some other things to think about:
enable runtime checking
review all your compiler's warnings.
Try to compile with a different delphi version or FPC. Other compilers/rtls/heapmanagers have different layouts, and that could lead to the error being caught easier.
If that all yields nothing, try to simplify the application till it goes away. Then investigate the most recent commented/ifdefed parts.
The first thing I would do is add MadExcept to your application and get a stack traceback that prints out the exact calling tree, which will give you some idea what is going on here. Instead of a random exception and a binary/hex memory address, you need to see a calling tree, with the values of all parameters and local variables from the stack.
If I suspect memory corruption in a structure that is key to my application, I will often write extra code to make tracking this bug possible.
For example, in memory structures (class or record types) can be arranged to have a Magic1:Word at the beginning and a Magic2:Word at the end of each record in memory. An integrity check function can check the integrity of those structures by looking to see for each record Magic1 and Magic2 have not been changed from what they were set to in the constructor. The Destructor would change Magic1 and Magic2 to other values such as $FFFF.
I also would consider adding trace-logging to my application. Trace logging in delphi applications often starts with me declaring a TraceForm form, with a TMemo on there, and the TraceForm.Trace(msg:String) function starts out as "Memo1.Lines.Add(msg)". As my application matures, the trace logging facilities are the way I watch running applications for overall patterns in their behaviour, and misbehaviour. Then, when a "random" crash or memory corruption with "no explanation" happens, I have a trace log to go back through and see what has lead to this particular case.
Sometimes it is not memory corruption but simple basic errors (I forgot to check if X is assigned, then I go dereference it: X.DoSomething(...) that assumes X is assigned, but it isn't.
I Noticed that a timer is in the stack trace.
I have seen a lot of strange errors where the cause was the timer event is fired after the form i free'ed.
The reason is that a timer event cound be put on the message que, and noge get processed brfor the destruction of other components.
One way around that problem is disabling the timer as the first entry in the destroy of the form. After disabling the time call Application.processMessages, so any timer events is processed before destroying the components.
Another way is checking if the form is destroying in the timerevent. (csDestroying in componentstate).
Can you post the sourcecode of this procedure?
BoldSystem.TBoldMember.CalculateDerivedMemberWithExpression
(BoldSystem.pas:4016)
So we can see what's happening on line 4016.
And also the CPU view of this function?
(just set a breakpoint on line 4016 of this procedure and run. And copy+paste the CPU view contents if you hit the breakpoint). So we can see which CPU instruction is at address 00404E78.
Could there be a problem with re-entrant code?
Try putting some guard code around the TTimer event handler code:
procedure TAttracsTimerDataModule.AttracsTimerTimer(ASender: TObject);
begin
if FInTimer then
begin
// Let us know there is a problem or log it to a file, or something.
// Even throw an exception
OutputDebugString('Timer called re-entrantly!');
Exit; //======>
end;
FInTimer := True;
try
// method contents
finally
FInTimer := False;
end;
end;
N#
I think there is another possibility: the timer is fired to check if there are "Dangling Logon Sessions". Then, a call is done on a TLogonSession object to check if it may be dropped (_GetMayDropSession), right? But what if the object is destroyed already? Maybe due to thread safety issues or just a .Free call and not a FreeAndNil call (so a variable is still <> nil) etc etc. In the mean time, other objects are created so the memory gets reused. If you try to acces the variable some time later, you can/will get random errors...
An example:
procedure TForm11.Button1Click(Sender: TObject);
var
c: TComponent;
i: Integer;
p: pointer;
begin
//create
c := TComponent.Create(nil);
//get size and memory
i := c.InstanceSize;
p := Pointer(c);
//destroy component
c.Free;
//this call will succeed, object is gone, but memory still "valid"
c.InheritsFrom(TObject);
//overwrite memory
FillChar(p, i, 1);
//CRASH!
c.InheritsFrom(TObject);
end;
Access violation at address 004619D9 in module 'Project10.exe'. Read of address 01010101.
Isn't the problem that "_GetMayDropSession" is referencing a freed session variable?
I have seen this kind of errors before, in TMS where objects were freed and referenced in an onchange etc (only in some situations it gave errors, very difficult/impossible to reproduce, is fixed now by TMS :-) ). Also with RemObjects sessions I got something similar (due to bad programming bug by myself).
I would try to add a dummy variable to the session class and check for it's value:
public variable iMagicNumber: integer;
constructor create: iMagicNumber := 1234567;
destructor destroy: iMagicNumber := -1;
"other procedures": assert(iMagicNumber = 1234567)