Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2006.07.02;
Скачать: CL | DM;

Вниз

Причина ошибки Access Violation   Найти похожие ветки 

 
Balkon   (2006-06-09 14:03) [0]

Здраствуйте.
В многопоточном приложении иногда (повторяемости пока добиться не удалось) возникает Access Violation по одному и томуже адресу. Алгоритмы все пересмотрены, доступ к совместным данным в критических секциях.... Подскажите идею, как отловить причину ошибки или отследить участок кода где это происходит?
Спасибо за внимание.


 
Сергей М. ©   (2006-06-09 14:05) [1]


> отследить участок кода где это происходит?


см. меню Search -> Find Error..


 
Kolan ©   (2006-06-09 14:09) [2]

MemProof...


 
Balkon   (2006-06-09 14:39) [3]

Спасибо.
Search -> Find Error привело меня в окно CPU...
А отследить участок кода паскалевского возможо?


 
Сергей М. ©   (2006-06-09 14:53) [4]

А ты какой адрес вводил ?
Из там два, в сообщении об AV, нужно вводить первый ..

Кстати, чему он равен ? Это важно ..


 
Balkon   (2006-06-09 15:01) [5]

Первый: 00402A0D
Второй: 1000FFFC

вводил первый


 
Balkon   (2006-06-09 15:11) [6]

Прошу прощения, вываливаюсь из форума.
Если не затруднит оставьте сообщение.
Прочитаю по прибытии.

Заранее спасибо.


 
Сергей М. ©   (2006-06-09 15:11) [7]

Хм..

$00402A0D у меня (тот же D7) указывает на вполне конкретнуб строчку:

if IsMultiThread then LeaveCriticalSection(heapLock); (GETMEM.INC)

И это , кстати, кое о чем уже говорит...

Как создашь потоки  - CreateThread ? BeginThread ? Класс TThread ?


 
Balkon   (2006-06-13 06:17) [8]

Приветствую.
Потоки создаю с помощью CreateThread. Класс TThread.

Обмен данными между потоками происходит через очереди команд и ответов,
которые описаны в следующем классе. Может в нем чтото не так?

//TThreadQueue
procedure TThreadQueue.Clear;
begin
 LockQueue;
 try
   While FQueue.Count > 0 do
     FQueue.Pop;
 finally
   UnlockQueue;
 end;
end;

function TThreadQueue.Count;
begin
 LockQueue;
 try
   Result := FQueue.Count;
 finally
   UnlockQueue;
 end;
end;

function TThreadQueue.Push(AItem: Pointer): Pointer;
begin
 LockQueue;
 try
   Result := FQueue.Push(AItem);
 finally
   UnlockQueue;
 end;
end;

function TThreadQueue.Pop: Pointer;
begin
 LockQueue;
 try
   Result := FQueue.Pop;
 finally
   UnlockQueue;
 end;
end;

function TThreadQueue.Peek: Pointer;
begin
 LockQueue;
 try
   Result := FQueue.Peek;
 finally
   UnlockQueue;
 end;
end;

procedure TThreadQueue.LockQueue;
begin
 EnterCriticalSection(FLock);
end;

procedure TThreadQueue.UnlockQueue;
begin
 LeaveCriticalSection(FLock);
end;

constructor TThreadQueue.Create;
begin
 inherited Create;
 InitializeCriticalSection(FLock);
 FQueue := TQueue.Create;
end;

destructor TThreadQueue.Destroy;
begin
 LockQueue;
 try
   FQueue.Free;
   inherited Destroy;
 finally
   UnlockQueue;
   DeleteCriticalSection(FLock);
 end;
end;


 
Balkon   (2006-06-13 06:29) [9]

 TThreadQueue = class
 private
   FQueue: TQueue;
   FLock: TRTLCriticalSection;
   procedure LockQueue;
   procedure UnlockQueue;
 public
   constructor Create;
   destructor Destroy; override;
   function Push(AItem: Pointer): Pointer;
   function Pop: Pointer;
   function Peek: Pointer;
   function Count: integer;
   procedure Clear;
 end;


 
Сергей М. ©   (2006-06-13 08:13) [10]


> Потоки создаю с помощью CreateThread. Класс TThread


Так все-таки CreateThread тобой явный вызывается или ты используешь класс TThread ?
Это принципиально ..


 
Balkon   (2006-06-13 08:20) [11]

Сорри...
Использую класс.


 
Сергей М. ©   (2006-06-13 08:28) [12]

Сборку проекта осуществляешь со всеми опциями отладки ?


 
Balkon   (2006-06-13 08:42) [13]

На вкладке Linker повесил два последних флажка. (До этого были сняты)
Это имеется ввиду?

Теперь при той самой ошибке приложение влетает в asm вставку процедуры Move модуля System.


 
Сергей М. ©   (2006-06-13 08:50) [14]

Не на вкладке "Linker", а на вкладке "Compiler" !

см. справку по теме "debugging, generating debug information"


 
Balkon   (2006-06-13 09:00) [15]

Справку прочел, спасибо.
На панели Debugging вкладки Compiler установлены все флажки.


 
Сергей М. ©   (2006-06-13 09:02) [16]

Что теперь показывает [1] ?


 
Balkon   (2006-06-13 09:06) [17]

asm вставка процедуры Move модуля System


 
Balkon   (2006-06-13 09:12) [18]

регулярно появляющийся код 00402A0D - вдет к asm вставке процедуры Move модуля System

идиножды возникший 004022E4 - в процедуру SysGetMem из упомянутого GETMEM.INC


 
Сергей М. ©   (2006-06-13 09:15) [19]

На какую конкретно строчку ?


 
Сергей М. ©   (2006-06-13 09:19) [20]

Вообще покажи код своего поточного класса ..
Кр.того покажи код создания-уничтожения экз-ров этого класса ..


 
Balkon   (2006-06-13 09:22) [21]

00402A0D:
 
 ...
 @@down:
       LEA     ESI,[ESI+ECX-4] { point ESI to last dword of source     }
       LEA     EDI,[EDI+ECX-4] { point EDI to last dword of dest       }

       SAR     ECX,2           { copy count DIV 4 dwords       }
       JS      @@exit
       STD
       REP     MOVSD

       MOV     ECX,EAX
       AND     ECX,03H         { copy count MOD 4 bytes        }
       ADD     ESI,4-1         { point to last byte of rest    }
       ADD     EDI,4-1
       REP     MOVSB
       CLD


004022E4:

    if f <> nil then begin
       u := PUsed(PChar(f) + size);
       u.sizeFlags := u.sizeFlags and not cPrevFreeFlag;
       next := f.next;
       if next = f then
         smallTab[size div cAlign] := nil
       else begin
         smallTab[size div cAlign] := next;
         prev := f.prev;
         prev.next := next;
         next.prev := prev;
       end;
       u := PUsed(f);
       u.sizeFlags := f.size or cThisUsedFlag;
       result := PChar(u) + sizeof(TUsed);
       Inc(AllocMemCount);
       Inc(AllocMemSize,size - sizeof(TUsed));
       exit;
     end;


 
Balkon   (2006-06-13 10:00) [22]

Поточный класс:
unit Unit_AKRThread;

interface

type
 TAKRThread = class(TThread)
 private
   FOwner: THandle;
   FComNumber: Integer;
   FPortHandle: THandle;
   FComandPackage: TAKRPackage;
   FAnswerPackage: TAKRPackage;
   FAnswerChecked: Boolean;
   FStackPackage: TStackPackage;
   FAnswerQueue: TThreadQueue;
   FRepeatCount: integer;
 protected
   procedure SendAnswer;
   procedure AddAnswerToQueue(AnswerPackage: TAKRPackage);
   function GetCantContactPackage: TAKRPackage;

   procedure OpenPort(ComNumber: Integer);
   procedure ClosePort;
   зrocedure ApplyComSettings;

   function WriteAKRComand: boolean;
   procedure RepeatAKRComand;

   procedure CheckBytes(TmpPackage: TReadPackage; Count: Word);
   function CheckTheAnswer(AnswerPackage: TAKRPackage): boolean;
   function ExtractAnswerPackage(StackPackage: TStackPackage;
     var AnswerPackage: TAKRPackage): boolean;
 protected
   procedure CleanUpThread(Sender: TObject);
   procedure Execute; override;
 public
   constructor Create(CreateSuspended: boolean; AOwner:THandle;
     AComNumber: Integer; AAnswerQueue: TThreadQueue; AComandPackage: TAKRPackage);
 end;

implementation

uses SysUtils,Unit_AKRManager,Unit_DeviceManager,Forms;

procedure TAKRThread.CleanUpThread(Sender: TObject);
begin
 ClosePort;
end;

procedure TAKRThread.SendAnswer;
begin
 if FAnswerChecked then
   AddAnswerToQueue(FAnswerPackage)
 else
   AddAnswerToQueue(GetCantContactPackage);
 Terminate;
end;

procedure TAKRThread.AddAnswerToQueue(AnswerPackage: TAKRPackage);
var
 P: PAKRPackage;
begin
 New(P);
 P^ := AnswerPackage;
 FAnswerQueue.Push(P);
 PostMessage(FOwner,WM_CANGETAKRANSWER,0,0);
end;

procedure TAKRThread.RepeatAKRComand;
begin
 if FRepeatCount <= 3 then
 begin
   inc(FRepeatCount);
   WriteAKRComand;
 end
 else
   SendAnswer;
end;

function TAKRThread.WriteAKRComand: boolean;
var
 wParam,lParam: Integer;

 Signaled, RealWrite, BytesTrans : Cardinal;
 WriteOL : TOverLapped;
begin
 FillChar(WriteOL, SizeOf(WriteOL), 0);
 WriteOL.hEvent:= CreateEvent(nil, True, True, nil);
 Try
   WriteFile(FPortHandle, FComandPackage, SizeOf(FComandPackage), RealWrite, @WriteOL);
   Signaled:= WaitForSingleObject(WriteOL.hEvent, INFINITE);
   Result := (Signaled = WAIT_OBJECT_0) and
     GetOverlappedResult(FPortHandle, WriteOL, BytesTrans, False);
 Finally
   CloseHandle(WriteOL.hEvent);
 End;
end;

constructor TAKRThread.Create(CreateSuspended: boolean; AOwner:THandle;
     AComNumber: Integer; AAnswerQueue: TThreadQueue; AComandPackage: TAKRPackage);
begin
 inherited Create(true);

 OpenPort(AComNumber);

 FOwner := AOwner;
 FAnswerQueue := AAnswerQueue;
 FComandPackage := AComandPackage;
 FRepeatCount := 0;

 FAnswerChecked := false;
 OnTerminate := CleanUpThread;

 if not CreateSuspended then
   Resume;
end;

procedure TAKRThread.Execute;
Var CurrentState : TComStat;
   AvaibleBytes, ErrCode, RealRead : Cardinal;
   ReadOL : TOverLapped;
   Signaled, Mask : DWORD;
   BytesTrans : DWORD;

   TmpPackage: TReadPackage;
Begin
 if WriteAKRComand then
 Try
   FillChar(ReadOL, SizeOf(ReadOL), 0);
   ReadOL.hEvent:= CreateEvent(nil, True, True, nil);
   SetCommMask(FPortHandle, EV_RXCHAR);
   while (not Terminated) do
   begin
     WaitCommEvent(FPortHandle, Mask, @ReadOL);
     Signaled:= WaitForSingleObject(ReadOL.hEvent, 1000);
     if (Signaled  = WAIT_OBJECT_0)and
       GetOverlappedResult(FPortHandle, ReadOL, BytesTrans, False) then
     begin
       If (Mask and EV_RXCHAR) <> 0 then
       begin
         ClearCommError(FPortHandle, ErrCode, @CurrentState);
         AvaibleBytes:= CurrentState.cbInQue;
         if (AvaibleBytes > 0) and
           ReadFile(FPortHandle, TmpPackage, AvaibleBytes, RealRead, @ReadOL) then
             CheckBytes(TmpPackage,AvaibleBytes);
       End;
     end
     else
       RepeatAKRComand;
   End;
 Finally
   CloseHandle(ReadOL.hEvent);
   SetCommMask(FPortHandle, 0);
 End;
End;

procedure TAKRThread.CheckBytes(TmpPackage: TReadPackage; Count: Word);
var
 i: integer;
 AnswerPackage: TAKRPackage;
begin
 for i := 1 to Count do
 begin
   SetLength(FStackPackage,Length(FStackPackage)+1);
   FStackPackage[Length(FStackPackage)-1] := TmpPackage[i];
 end;

 if ExtractAnswerPackage(FStackPackage,AnswerPackage) then
   if CheckTheAnswer(AnswerPackage) then
     SendAnswer
   else
     RepeatAKRComand;
end;

procedure TAKRThread.OpenPort(ComNumber: Integer);
Var
 ComName : String;
Begin
ComName:= Format("\\.\COM%-d", [ComNumber]);
FPortHandle:= CreateFile(
           PChar(ComName), GENERIC_READ or GENERIC_WRITE,
           0, nil, OPEN_EXISTING,
           FILE_ATTRIBUTE_NORMAL or FILE_FLAG_OVERLAPPED, 0) ;
ApplyComSettings;
end;

procedure TAKRThread.ClosePort;
begin
CloseHandle(FPortHandle);
FPortHandle:= INVALID_HANDLE_VALUE;
end;

...

end.


 
Balkon   (2006-06-13 10:07) [23]

Создается и живет этот поток для обработки одной команды и ответа на нее.
Создается он в "буферном" потоке (обрабатывающем командную очередь):

FAKRThread := TAKRThread.Create(false,FOwner,ComNumber,FAnswerQueue,ComandPackage);
 FAKRThread.WaitFor;


 
Сергей М. ©   (2006-06-13 10:13) [24]

см.
http://delphimaster.net/view/4-1149756324/
[14]


 
Balkon   (2006-06-13 12:32) [25]

Переписал функцию записи команды в порт, проверяя результат
WriteFile и количества записанных байт.

function TAKRThread.WriteAKRComand: boolean;
var
 wParam,lParam: Integer;
 Signaled, RealWrite, BytesTrans : Cardinal;
 Success : boolean;
 WriteOL : TOverLapped; {структура для асинхронной записи}
begin
 {создание события для асинхронной записи}
 Result := false;
 FillChar(WriteOL, SizeOf(WriteOL), 0);
 WriteOL.hEvent:= CreateEvent(nil, True, True, nil);
 Try
   {начало асинхронной записи}
   Success := WriteFile(FPortHandle, FComandPackage, SizeOf(FComandPackage),
     RealWrite, @WriteOL) or (GetLastError = ERROR_IO_PENDING);
   if Success then
   begin
   {ожидания завершения асинхронной операции}
     Signaled:= WaitForSingleObject(WriteOL.hEvent, INFINITE);
     {получение результата асинхронной операции}
     Result := (Signaled = WAIT_OBJECT_0) and
       GetOverlappedResult(FPortHandle, WriteOL, BytesTrans, False) and
         (SizeOf(FComandPackage) = BytesTrans);
   end
   else
     raise Exception.Create("Ошибка записи команды: " + IntToStr(GetLastError));
 Finally
   CloseHandle(WriteOL.hEvent);{освобождение дескриптора события}
 End;
end;


и основную процедуру с чтением данных, проверяя результат ReadFile.

procedure TAKRThread.Execute;
Var CurrentState : TComStat;
   AvaibleBytes, ErrCode, RealRead : Cardinal;
   ReadOL : TOverLapped;
   Signaled, Mask : DWORD;
   BytesTrans : DWORD;
   Success: Boolean;
   TmpPackage: TReadPackage;
Begin
 if WriteAKRComand then
 Try
   FillChar(ReadOL, SizeOf(ReadOL), 0);
   ReadOL.hEvent:= CreateEvent(nil, True, True, nil);
   SetCommMask(FPortHandle, EV_RXCHAR);
   while (not Terminated) do
   begin
     WaitCommEvent(FPortHandle, Mask, @ReadOL);
     Signaled:= WaitForSingleObject(ReadOL.hEvent, 1000);
     if (Signaled  = WAIT_OBJECT_0) and
       GetOverlappedResult(FPortHandle, ReadOL, BytesTrans, False) then
     begin
       If (Mask and EV_RXCHAR) <> 0 then
       begin
         ClearCommError(FPortHandle, ErrCode, @CurrentState);
         AvaibleBytes:= CurrentState.cbInQue;
         if (AvaibleBytes > 0) then
         begin
           Success := ReadFile(FPortHandle, TmpPackage, AvaibleBytes, RealRead, @ReadOL)
             or (GetLastError = ERROR_IO_PENDING);
           if Success then
             CheckBytes(TmpPackage,AvaibleBytes)
           else
             raise Exception.Create("ошибка чтения данных: " + IntToStr(GetLastError));
         end;
       End;
     end
     else
       RepeatAKRComand;
   End;
 Finally
   CloseHandle(ReadOL.hEvent);
   SetCommMask(FPortHandle, 0);
 End;
End;


Учтены ли таким образом замечания по Read/WriteFile?
>>Отсутствует анализ результатов, возвращаемых ф-циями Read/WriteFile, WaitCommEvent

Не совсем понятно как проверять результат WaitCommEvent, возращаемый результат аналогичным Read/WriteFile образом?


 
Сергей М. ©   (2006-06-13 12:55) [26]


> Учтены ли таким образом замечания по Read/WriteFile?


Учтены, но логически неверно.

Если ReadFile() вернула False (код ошибки ERROR_IO_PENDING), бессмысленно вызывать CheckBytes(), ибо операция чтения не завершена (находится в стадии выполнения).

В противном случае CheckBytes() следует вызывать со вторым параметром, равным RealRead (фактически прочитанное число байт), а не AvaibleBytes (ожидаемое к прочтению число байт).

Возбуждение исключения в поточной ф-ции без предусмотренной его последующей обязательной обработки недопустимо. В простейшем случае тело метода Execute() следует заключить в try..except - блок, дабы избежать непредсказуемых последствий.

Результат вызова WaitCommEvent() следует обрабатывать похожим образом, в справке это описано.


 
Balkon   (2006-06-13 13:23) [27]

Спасибо большое!
Разбираюсь дальше...



Страницы: 1 вся ветка

Текущий архив: 2006.07.02;
Скачать: CL | DM;

Наверх




Память: 0.55 MB
Время: 0.045 c
6-1140525744
AlexEgorov
2006-02-21 15:42
2006.07.02
Почему не получается получить адрес хоста для TCP/IP порта


2-1150125729
AlexanderMS
2006-06-12 19:22
2006.07.02
Отличие Packed Record от просто Record.


15-1149483452
Ega23
2006-06-05 08:57
2006.07.02
С Днём рождения! 3 июня


3-1146726113
VadimSpb
2006-05-04 11:01
2006.07.02
Изменение типа поля


15-1149595640
Marser
2006-06-06 16:07
2006.07.02
История повторяется...