Q&A

  • InsertFree 에러에 대한 질문
안녕하세요. 어제 System 명령 사용에 대한 질문을 했는데
제가 사용했던 소스를 다시 올리겠습니다.

현재 제가 개발한 프로그램이 20대 정도에서 운용하고 있는데,
평균 일주일에 2, 3번 정도 프로그램이 다운되네요.
장비 사양 및, 장비 부하등은 똑 같다고 보시면 됩니다.

일단 제가 개발한 환경은
- Windows 2000 Pro
- Delphi 5

그리고 프로그램 주요 특징 으로는
- 120 개의 스래드가 사용이 됨
- 스래드 멤버 변수도 있지만 전역 변수에 참조 되는 일도 있음.
- 전역 변수는 각각의 스래드가 사용될 수 있도록 120개의 개체가 저장된 TStringList 로 구성됨.


//----------------------------------------------------------------------------------------------------------------------//
// 첫번째의 경우
//----------------------------------------------------------------------------------------------------------------------//

* Doctor Watson Log

스레드 Id 0x708의 상태 덤프

eax=20162112 ebx=16262190 ecx=00000035 edx=00000008 esi=00000020 edi=16262190
eip=00401fdb esp=0642e97c ebp=005bd488 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206


함수: <nosymbols>
        00401fbb c1fa02           sar     edx,0x2
        00401fbe a190d45b00       mov     eax,[005bd490]         ds:005bd490=00136dc0
        00401fc3 8b4490f4         mov     eax,[eax+edx*4+0xf4]   ds:0086d5db=????????
        00401fc7 85c0             test    eax,eax
        00401fc9 7510             jnz     0040c0db
        00401fcb a190d45b00       mov     eax,[005bd490]         ds:005bd490=00136dc0
        00401fd0 895c90f4         mov     [eax+edx*4+0xf4],ebx   ds:0086d5db=????????
        00401fd4 895b04           mov     [ebx+0x4],ebx          ds:16acf762=????????
        00401fd7 891b             mov     [ebx],ebx              ds:16262190=7257202e
        00401fd9 eb3a             jmp     0040ab15
오류 -> 00401fdb 8b10             mov     edx,[eax]              ds:20162112=????????
        00401fdd 894304           mov     [ebx+0x4],eax          ds:16acf762=????????
        00401fe0 8913             mov     [ebx],edx              ds:16262190=7257202e
        00401fe2 8918             mov     [eax],ebx              ds:20162112=????????
        00401fe4 895a04           mov     [edx+0x4],ebx          ds:0086d5da=????????
        00401fe7 eb2c             jmp     0040a115
        00401fe9 81fe003c0000     cmp     esi,0x3c00
        00401fef 7c0d             jl      0040aafe
        00401ff1 8bd6             mov     edx,esi
        00401ff3 8bc7             mov     eax,edi
        00401ff5 e802ffffff       call    00401efc
        00401ffa 84c0             test    al,al

*----> 스택 역 추적 <----*

FramePtr ReturnAd Param#1  Param#2  Param#3  Param#4  Function Name
005BD488 00000000 00136DC0 00137FB4 00138164 00000000 !<nosymbols>

//----------------------------------------------------------------------------------------------------------------------//
// 소스 및 소스 설명
//----------------------------------------------------------------------------------------------------------------------//

* 120개의 스래드가 운용중에 있습니다. 여기서 trdChannel 은 스래드입니다.

* 각각의 스래드에서 사용하는 변수는 채널 변수와 전역 변수로 구성되여 있습니다.

* 채널 변수는

* 전역 변수는

* 전역 변수 선언은 gPortInfoList : TStringList 로 처리 했습니다.

* 전역 변수 초기 데이터 입력은 프로그램 시작시 다음과 같이 입력을 합니다.
  recPortInfo : TPortInfo;
  
  recPortInfo := TPortInfo.Create;
  
  recPortInfo.....  <- 데이터 초기값 입력
  recPortInfo.....  <- 데이터 초기값 입력
  recPortInfo.....  <- 데이터 초기값 입력
  
  gPortInfoList.AddObject(Trim(recPortInfo.PortExt), recPortInfo);
  
* TPortInfo 구조는 다음과 같은 타입으로 구성되여 있습니다.
     PortNo              : SmallInt
     PortExt                : ShortString
     PortAgentID        : ShortString
     PortPos                : ShortString
     PortACD                : ShortString
     PortDB                : ShortString
     StartFile                 : ShortString
     EndFile             : ShortString
     PortCount        : Integer
     StartSno                : Integer
     EndSno              : Integer
     ScenarioPath         : ShortString
     ScenarioDesc        : ShortString
     PortMode            : String
     TruncInfo                : String
     AniInfo                : String
     AniInfoDefault        : String
     DnisData                : String
     R2Tone                : String
     DnisDataDefault        : String
     ExtData                : String
     ExtDataDefault        : String
     CallData                : String
     CallDataDefault         : String;
     AcdStatus        : Integer;
     AcdStatusDefault        : Integer;
     AcdAgent                : Integer;
     AcdAgentDefault        : Integer;
     HeldCount        : Integer;
     HeldTime                : Integer;
     HeldServer        : Integer;
     TransferFlag         : Integer;
     SVFlag                 : Integer;
     strSVReturnCode        : String
     strSVScore         : String;
     Call_ID01                : String  
     Call_ID02                : String
     Call_ID03         : string;
     ExtraData         : string;
     ExtraODNData         : string;
     PartyDeleted        : String
     PartyChanged         : String;
     ServiceStart        : String  
     ServiceStop         : String;
     PortLock            : Boolean;
     PortState           : Byte;  
     iSleepCnt                 : Integer;
     bCTIDisconnect         : Boolean;
     PortActionFlg           : Boolean;
     PortApplyCheck          : Boolean;
     PortMentApplyCheck : Boolean;
     PortMentActionFlg  : Boolean;
     PortStopCheck           : Integer;
     bMohHangup         : Boolean;
     vMohThread         : TMOHThread;
     PortApplyCompleteFlag : Boolean;
     CTIDisconnectEvent : Integer;
     iCSNo                : Integer
     iCSEvent                 : Integer;
     strCSFile                : String
     strCSHour         : String;
     CallStartTime         : String;
     strPlayFile         : String;
     strDigitCount         : String;
     strTimeout         : String;
     strBaseScore         : String;
     strThisDN                : String
     strOtherDN        : String
     strANI                 : String;
     bEventPartyDeleted : Boolean;
     bMOHThreadDelay         : Boolean;
  

* 이 부분에서 처리할때 InsertFree 에러가 나오는 것 같습니다.

// 스래드가 시작할 때 맨처음에 수행되는 모듈입니다.
procedure trdChannel.Service_Start(bFlag : Boolean);
var curDate: TDateTime;
    strLog, strDesc, strResult : String;
    iRc : Integer;
begin

    curDate := Now;
    
    EnterCriticalSection(gPortCritical);
        with TPortInfo(gPortInfoList.Objects[t_Index]) do
        begin
          Inc(PortCount);
          ServiceStart := FormatDateTime('dd hh:nn:ss', curDate);
          ServiceStop  := '';
          
          // CallTrace 시간 형식 변경.
          CallStartTime := FormatDateTime('yyyymmddhhnnss',curDate);

          // Channel Status display
          Set_StateCount(t_Index, PortState, STATUS_ANSWER);
          PortState := STATUS_ANSWER;
        end;
    LeaveCriticalSection(gPortCritical);
    
end;

// 화면상에 정보를 보여주기 위하여 Count 정보를 증가 및 변동하는 모듈입니다.
procedure Set_StateCount(iIndex, iOldState, iNewState : Integer);
var vInx : Integer;
    curTime : String;
begin
    EnterCriticalSection(gGlobalCountCritical);

    TPortInfo(gPortInfoList.Objects[iIndex]).PortState := iNewState;
    Dec(gStatusCount[iOldState]);
    Inc(gStatusCount[iNewState]);

    case iNewState of
         STATUS_ANSWER : begin
                             // Peak Time Count
                             curTime := FormatDateTime('hhnn', Now);

                             // 시간대별 Call Count 누적
                             vInx := StrToInt(FormatDateTime('hh', Now));
                            
                             Inc(gTimeCount[vInx]);
                            
                             // Acd Group별 Call Count 누적
                             vInx := gAcdGroup.IndexOf(TPortInfo(gPortInfoList.Objects[iIndex]).PortACD);
                             if vInx > -1 then
                                 Inc(TAcdGroupClass(gAcdGroup.Objects[vInx]).CallCount);
                         end;
    end;

    LeaveCriticalSection(gGlobalCountCritical);
end;



//----------------------------------------------------------------------------------------------------------------------//
// 두번째의 경우
//----------------------------------------------------------------------------------------------------------------------//


* 두번째의 경우는 닥터왓슨 로그는 나오지 않았지만 InsertError에 대하여 Try Except 가 발생한 부분입니다.
  아래의 로직을 수행하면서 E.Message 내용이 나왔습니다. 해당 Address가 InsertFree 입니다.
  (Access violation at address 00401FDB in module 'MT.exe'. Read of address 20162112)
  그래서 Try Except 안에 있는 부분에서 잘못된 것이라고 생각합니다.

// 사용자 정의 변수의 값을 지정한 값으로 초기화 한다.
//    Data1 : 사용자 정의 변수
//    Data2 : 초기값

function FCN_Initialize(iIndex, iPort, iSnoNo, iSnoType: Integer; sSnoPath, sSnoFile: String): Integer;
var
    recFuncTable: TFuncTableRecord;
    iPos1 : integer;
    strLog, strResult, sResult, strOne1, strData1 : string;
    lOk : Boolean;
begin

    // FuncTable Table에서 값을 가져온다.
    if not Read_FuncTable(iIndex, iPort, iSnoNo, sSnoFile, recFuncTable) then
    begin
        Result := EVT_SQL_ERROR;
        WriteTraceLog(ALL_LOG, iPort, iSnoNo, iSnoType, Result, 'FCN_Initialize: EVT_SQL_ERROR');
        exit;
    end;

    strLog := '';
    lOk := True;
    
    
    try

        strData1 := recFuncTable.strData1;
        if (not IsNull(strData1)) then
            if (strData1[Length(strData1)] <> UDV_END_DELI_COMMA) then strData1 := strData1 + UDV_END_DELI_COMMA;

        while strData1 <> '' do
        begin
            iPos1 := Pos(UDV_END_DELI_COMMA, strData1);
            if iPos1 < 1 then Break;

            strOne1 := Copy(strData1, 1, iPos1-1); //UDV Value
            if strOne1 = '' then Break;

            // 전역 변수 저장함. gPortInfoList 의 iIndex에 해당하는 개체에 데이터 저장
            Result := Set_UdvValue(iIndex, Trim(strOne1), '',
                        'FCN_Initialize', EVT_NEXT_SCENARIO, EVT_REALCT_HANGUP);

            if Result <> EVT_NEXT_SCENARIO then
            begin
                lOk := False;
                break;
            end;

            Delete(strData1, 1, iPos1);
            Sleep(1);
        end;

    except
        on E: Exception do
        begin
            strResult := E.Message;

            Result := EVT_REALCT_HANGUP;
            strLog := 'FCN_Initialize: Except Error *##*-' + strResult;
            WriteTraceLog(ALL_LOG, iPort, 0, 0, 0, strLog);
            exit;
        end;
    end;
end;

// gPortList 는 시스템 시작시 각각의 스래드 ID 값을 저장시킵니다.
   vPort := trdChannel.Create();
   gPortList.AddObject(FormatFloat('0000', iPortIndex{n-1}), vPort);

// gUserVarList 는 시스템 시작시 각각의 스래드가 가지고 있어야할 기본 정보를 저장하는 리스트입니다.
   recUdvClass := TUDVRecord.Create;
   with recUdvClass do
   begin
       // 자신의 채널에서 쓸 UDV를 저장한다.
       Name        := FieldByName('Name').AsString;
       DataType := FieldByName('DataType').AsString;
       InitData    := FieldByName('InitData').AsString;
       Des           := FieldByName('Des').AsString;

       gUserVarList.AddObject(UpperCase(Name), recUdvClass);
   end;
      
// m_UdvList 는 각각의 스래드가 가지고 있는 멤버 변수입니다. 이 값은 gUserVarList 값을 처음에 초기화 합니다.
// 그래서 스래드가 처음에 시작할 때 초기 값을 gUserVarList 을 이용하면서 로직을 수행하다가
// 다기 처음에 수행할 때 값을 다시 초기화 합니다.


// 스래드가 수행중에 스래드가 가지고 있는 멤버 변수 값을 Write 하는 모듈입니다.
function Set_UDVValue(const iIndex : Integer; const vUdv : String; const vData : String;
                      const sFuncName : String; const iSuccess : Integer; const iFail : Integer) : Integer;
var vInx : Integer;
    recUdv : TUDVRecord;
begin
    Result := iSuccess;
    try
        vInx := Get_UDVIndex(iIndex, Trim(vUDV));
        if vInx > -1 then
        begin
            recUdv := TUdvRecord(trdChannel(gPortList.Objects[iIndex]).m_UdvList.Objects[vInx])
            recUdv.InitData := vData;
        end else
        begin
          Result := iFail;
        end;
    except
        Result := iFail;
    end;
end;

function Get_UDVIndex(const iIndex : Integer; const vUDV : String) : Integer;
begin
    Result := -1;

    if( vUDV <> '') then
    begin
        if vUDV[1] = UDV_START_DELI_AT then
            Result := gUserVarList.IndexOf(Copy(vUDV, 2, Length(vUDV)));
    end;

end;


0  COMMENTS