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

Вниз

Тестирование программного обеспечения   Найти похожие ветки 

 
31512   (2007-10-15 11:25) [0]

Здравствуйте уважаемые. Обращаюсь за вашим опытом в тестировании и отладке ПО. Итак имеется некоторая программа. Многопоточная, использующая сокеты для обмена данными по локальной сети.
Требование к работе программы простое: стабильная работа всегда. Тесты показывают, что программа может проработать 10 - 20 часов без единого сбоя. Потом может свалиться с Access violation. А может не свалиться. А может свалиться через 2 часа. Или через час. Короче - ошибка эта нестабильна. В процессе отладок и тестирования было выявлено и устранено много сторонних дефектов, приведщих к ускорению работы, более оптимальному расходу памяти и другие. Но устранить нестабильный Access violation пока не удалось. Программа написана на Delphi 7 (upd 1), работает под WinXP SP1 со всеми обновлениями. Поиски в интернете не увенчались успехом. Интерисует: любой опыт, даже случайный, при работе с системами такого рода. Подробности при обсуждении. Спасибо.


 
Anatoly Podgoretsky ©   (2007-10-15 11:49) [1]

> 31512  (15.10.2007 11:25:00)  [0]

В интернете по этой проблемы гигатонны информации, все они сводятся к одному - неверное значение указателя.
При выдаче данного сообщения выдается и сопутствующая информация.


 
31512   (2007-10-15 11:51) [2]


> Anatoly Podgoretsky ©   (15.10.07 11:49) [1]

Нет работы с указателями.


 
KSergey ©   (2007-10-15 11:53) [3]

> 31512   (15.10.07 11:25)  
> Подробности при обсуждении.

Это о чем??
А вообще - по описанию ошибки очевидно, что есть кривость дизайна. И где-то что-то когда-то в этих потоках не срастается со ссылками на созданные/уничтоженные объекты.
Изучайте архитектуру своего приложения и обмозговывайте различные ситуации.
Не забывайте учитывать при этом, что переключение между потоками выполняется в совершенно произвольный момент, а вовсе не тогда, когда вы об этом думаете.


 
KSergey ©   (2007-10-15 11:54) [4]

> 31512   (15.10.07 11:51) [2]
> Нет работы с указателями.

Указатель здесь - ссылка на объект.
Я не поверю, что вы в своей программе не создаете ни одного наследника TObject.


 
31512   (2007-10-15 11:56) [5]


> KSergey ©   (15.10.07 11:53) [3]

Спасибо за совет. Про подробности я имел ввиду, что отвечу на вопросы, на которые смогу ответить, касательно устройства программы.


 
31512   (2007-10-15 11:59) [6]


> KSergey ©   (15.10.07 11:54) [4]

Философия меня интерисует. Я имел ввиду явное использование pointer.
Что такое указатель я знаю. Спасибо.


 
Сергей М. ©   (2007-10-15 12:23) [7]


> устранить нестабильный Access violation пока не удалось


Интересны подробности неудачных попыток - что, где, при каких условиях осуществлялось при этих попытках ..


 
Anatoly Podgoretsky ©   (2007-10-15 12:26) [8]

> 31512  (15.10.2007 11:51:02)  [2]

Исключено, большинство АПИ функций работает с указателями.
Наверняка есть объекты, строки, динамические массивы, буферы.


 
clickmaker ©   (2007-10-15 12:29) [9]


> 31512   (15.10.07 11:25)

логи ведутся?


 
31512   (2007-10-15 13:09) [10]


> Сергей М. ©   (15.10.07 12:23) [7]



> Интересны подробности неудачных попыток - что, где, при
> каких условиях осуществлялось при этих попытках ..

Ок. Из последнего. Суть такая: программа общается через сокеты с серверами, установленными на других машинах. Она посылает серверам комманды, а обратно приходят ответы. Команда имеет формат: 1 байт код команды, остальное данные. Некоторые комманды могут не иметь данных, такие комманды пусть будут названы однобайтовыми. От сервера приходит ответ: 1 байт код ошибки (0 - всё хорошо, не 0 какая-то ошибка) - остальное данные. Для передачи комманды формируется TByteArray, куда и запихивается всё простым копированием.
Примеры:
Комманда для установки управляющего кода формируется функцией

function CreateCode(const Code : word) : TCommand;
begin
 Result := CreateCommand($28, SizeOf(Code), "Установка кода");
 Move(Code, Result.Buffer[0], Result.BufferLength);
end;

где

 TCommand = packed record
   Code : byte;        //код комманды
   Name : ShortString; //наименование комманды
   Buffer : TByteArray;//данные
   BufferLength : Word;//длина данных в байтах
 end;

и

function CreateCommand(const ACode : Byte; const ABufferLength : Word; const CommandName : string = "") : TCommand;
begin
 Result.Code := ACode;
 Result.Name := CommandName;
 Result.BufferLength := ABufferLength;
end;


при отсылке формируется пакетик:


function TConnection.SendCommand(Command: TCommand; Delay : integer = 0) : TAnswer;
var
Buffer : TByteArray;
i, Len : integer;
Value : byte;
begin
...    
  Len := Command.BufferLength + 1;
  Buffer[0] := Command.Code;
  if Command.BufferLength > 0 then
   for i:=1 to Command.BufferLength do
     begin
       Value := Command.Buffer[i - 1];
       Buffer[i] := Value;
     end;
  ClientSocket.SendBuf(Buffer, Len);
  Result := GetAnswer(Command);
...
end;


где,


 TAnswer = packed record
   ErrorCode : byte; //код ошибки
   Data : TByteArray;//данные
   DataLength : SmallInt;//длина данных
 end;


и


function TConnection.GetAnswer(const Command : TCommand): TAnswer;
var
Buffer : TByteArray;
BytesReceived : integer;
begin
  FillChar(Result.Data, SizeOf(Result.Data), 0);
  if FClientSocket.WaitForData(5000) then
  begin
    BytesReceived := FClientSocket.ReceiveBuf(Buffer, SizeOf(TByteArray));
    if BytesReceived > 0 then
    begin
       Result.DataLength := BytesReceived - 1;
       Result.ErrorCode := Buffer[0];
       RaiseException(Result.ErrorCode, Command);//генерация исключения в зависимости от кода ошибки
      try
        CheckData(Command, Buffer, BytesReceived);//проверка на длину данных, внутри при необходимости генерация исключения
        if Result.DataLength > 0 then Move(Buffer[1], Result.Data, Result.DataLength);
      except on E : Exception do
       begin
          //запись в лог
       end;
      end
    end else begin
                  //потеря связи. запись в лог
                end;
end;


Выснилось, что иногда с серверов приходят неполные данные. Срабатывает CheckData. Обработали и эту ситуацию. Иногда падает на однобайтовых коммандах.


> clickmaker ©   (15.10.07 12:29) [9]

Ведутся


> Anatoly Podgoretsky ©   (15.10.07 12:26) [8]

В таком случае, извиняюсь, я неправильно понял и указатели используются.


 
Сергей М. ©   (2007-10-15 13:19) [11]


> иногда с серверов приходят неполные данные


Абсолютно нормальная ситуация.


> Обработали и эту ситуацию


Показывай как ...

И всвязи с эти контрвоапрос - а начерта гонять по сети 32кб бестолковых данных (см. тип TByteArray), если из этих 32кб информацию несет, например, всего один байт (см., к примеру, BufferLength = 1 или DataLength = 1) ?


> SendBuf(Buffer, Len);


SendBuf - это функциональный метод.
В условиях неблокирующего режима и с учетом тех самых  32кб это крайне важно.


 
31512   (2007-10-15 13:23) [12]

Повторюсь ещё раз. Ошибка нестабильна. Нельзя предсказать в какой момент времени она произойдёт. Один и тот же участок (я имею ввиду именно пробленый, который я пока не выявил) кода может отработать 1000 раз без проблем, но n-ый раз свалиться. Могут ли быть подвохи со стороны операционной системы?


 
31512   (2007-10-15 13:29) [13]


> Сергей М. ©   (15.10.07 13:19) [11]

Ясно. Обработка имеет такой вид:

procedure CheckData(const Command : TCommand; Buffer : TByteArray; BytesReceived : integer);
var
Size : integer;
begin
case Command.Code of
 $21 : begin //состояние сепарации
         Size := SizeOf(TSeparateState) + 1;
         if BytesReceived <> Size then raise Exception.Create(Format("Несовпадение данных о сепарации! Ожидалось %d получено %d", [Size, BytesReceived]));
       end;
 $24 : begin //Контрольный спектр
         Size := SizeOf(TControlSpectrumData) + 1;
         if BytesReceived <> Size then raise Exception.Create(Format("Несовпадение данных о контрольном спектре! Ожидалось %d получено %d", [Size, BytesReceived]));
       end;
 $25: begin //Гистограмма
         Size := SizeOf(TGistogramm) + 1;
         if BytesReceived <> Size then raise Exception.Create(Format("Несовпадение данных о гистограмме! Ожидалось %d получено %d", [Size, BytesReceived]));
       end;
 $28 : begin//установка кода питателя
         Size := 1;
         if BytesReceived <> Size then raise Exception.Create(Format("Несовпадение данных при изменеии кода питателя! Ожидалось %d получено %d", [Size, BytesReceived]));
       end;
end;
end;


Вот кстати это и помогло выявить, что вместо 801 байт по гистограмме пришёл всего 1 байт с кодом ошибки 0.

У меня встречный вопрос, если не затруднит
1. почему абсолютно нормальная ситуация.
2. Разве передаётся не Len байт при SendBuf(Buffer, Len); Сервер отрабатывает коррекно.
3.
> SendBuf - это функциональный метод.
> В условиях неблокирующего режима и с учетом тех самых  32кб
> это крайне важно.

Подробности не помешают. Особенно почему ВАЖНО!


 
Сергей М. ©   (2007-10-15 13:30) [14]


> Могут ли быть подвохи со стороны операционной системы?
>


Не могут.

Проблема - в непонимании тобой работы транспорта, реализованного в классе TCustomWinSocket.


 
31512   (2007-10-15 13:33) [15]


> Сергей М. ©   (15.10.07 13:19) [11]

И ещё. Я сейчас перейду к указателям и опубликую код. Может лучше будет?


 
Сергей М. ©   (2007-10-15 13:35) [16]


> 1. почему абсолютно нормальная ситуация


Потому что используется поточный транспортный протокол.


> 2. Разве передаётся не Len байт при SendBuf(Buffer, Len);


Передается ровно столько, сколько вернул результат вызова этого функционального метода.


> Подробности не помешают


Справку уже проштудировал ?


 
31512   (2007-10-15 13:36) [17]


> Сергей М. ©   (15.10.07 13:30) [14]


> Проблема - в непонимании тобой работы транспорта, реализованного
> в классе TCustomWinSocket

Скорее всего ты прав. Я сейчас перепишу немного сообветсвующие участки кода.


 
Сергей М. ©   (2007-10-15 13:36) [18]


> перейду к указателям и опубликую код. Может лучше будет?


Во всяком случае не хуже)


 
31512   (2007-10-15 13:37) [19]


> Сергей М. ©   (15.10.07 13:35) [16]


> Справку уже проштудировал ?

Да. И сделал выводы.


 
Сергей М. ©   (2007-10-15 13:40) [20]


> 31512   (15.10.07 13:37) [19]


Тоже самое касается и ReceiveBuf.

Тот факт, что он вернул результат > 0, еще не говорит о том, что принято не иначе как SizeOf(TByteArray) ожидаемых тобой байт данных.


 
31512   (2007-10-15 13:48) [21]


> Сергей М. ©   (15.10.07 13:40) [20]

Это я давно понял, поэтому у меня и написано
BytesReceived := FClientSocket.ReceiveBuf(Buffer, SizeOf(TByteArray));
SizeOf(TByteArray) стоит чтобы было.
Вот, кстати, нельзя-ли от него вообще как-нибудь избавиться?


 
Сергей М. ©   (2007-10-15 13:54) [22]


> 31512   (15.10.07 13:48) [21]


> поэтому у меня и написано


Ты ожидаешь от сервера 32кб данных.
В рез-те вызова ReceiveBuf ты получил всего 8кб (> 0) данных.

Твои действия ?


 
Сергей М. ©   (2007-10-15 13:55) [23]


> нельзя-ли от него вообще как-нибудь избавиться?


А зачем он вообще фигурирует в структуре ? Чем это оправдано ?


 
Сергей М. ©   (2007-10-15 13:59) [24]


> Многопоточная, использующая сокеты для обмена данными по
> локальной сети.


В приведенном коде нет ни намека на его использование в контексте более чем одного потока.


 
31512   (2007-10-15 13:59) [25]


> Сергей М. ©   (15.10.07 13:54) [22]

Генерирую исключение. То, что использовать SizeOf(TByteArray) некорректно я понял. В этом конкретном случае нужно
BytesReceived := FClientSocket.ReceiveBuf(Buffer, 801);
это стало ясно. Или я не прав?


 
Сергей М. ©   (2007-10-15 14:04) [26]


> 31512   (15.10.07 13:59) [25]
>
>


> Генерирую исключение.


С какого перепугу ?


 
31512   (2007-10-15 14:09) [27]


> Сергей М. ©   (15.10.07 14:04) [26]

Объясню. Итак, я жду от сервера 801 байт. Но получаю 1 байт. Первый байт в ответе всегда результат выполнения комманды.  Если это не 0, то дальше и смотреть нечего - ошибка на сервере. Если оставшееся меньшее 800 байт - то исключение, поскольку должно быть 800 и никак не меньше. Или я тебя не так понял?


 
31512   (2007-10-15 14:20) [28]


> Сергей М. ©

Примерный вид такое пересылки будет более корректен?

var
 Tmp : TByteArray
 Buf : Pointer;
begin
 Tmp[0] := Command.Code;
 Move(Command.Buffer[0], Tmp[1], Command.BufferLength);
 Buf = AllocMem(Command.BufferLength + 1);
 Move(Buf, Tmp, Command.BufferLength + 1);
 ClientSocket.SendBuf(Buf, Command.BufferLength + 1);
end;


 
Сергей М. ©   (2007-10-15 14:20) [29]


> Если оставшееся меньшее 800 байт - то исключение, поскольку
> должно быть 800 и никак не меньше


Если это предусматривает прикладной протокол, то да.

А вот транспортный протокол волен передавать эти самые 800 байт так как ему вздумается: хоть одним целум "куском", хоть произвольным количеством "кусков" произвольного размера.

Обращаясь к ReceiveBuf-методу ты работаешь на уровне именно на транспортном уровне, поэтому не следует полагаться, что за один вызов этого метода ты получишь сразу все ошидаемые тобой 800 байт.


 
Сергей М. ©   (2007-10-15 14:23) [30]


> Примерный вид такое пересылки будет более корректен?


А это вообще полное безобразие)

До тех пор пока ты не определишься, зачем у тебя везде фигурирует тип TByteArray, ничего путного не получится.


 
Ping   (2007-10-15 15:28) [31]

Команда имеет формат: 1 байт код команды, остальное данные.

Ты уверен, что 1 байта достаточно? Не жалей байтиков, сделай cardinal, на будущее.

И меняй протокол:

Command -  4 байта    -  команда
Size        -  4 байта    -  размер пакета
Data       -  Size байт  -  пакет данных


(как-то так, как минимум...)

И принимай Size байтов в пакете данных. Не больше, и не меньше.


 
31512   (2007-10-16 07:09) [32]


> Ping   (15.10.07 15:28) [31]

Самое смешное, что когда-то я предлагал сделать именно так. Предлагал указывать размеры пакета или покрайней мере размеры его блоков. Но программист, который писал сервер наотрез отказался так делать, мотивируя, что у него уже всё написано. Теперь вот есть необльшая морока.


 
31512   (2007-10-16 07:14) [33]


> Сергей М. ©   (15.10.07 14:23) [30]

Согласен, безобразие... Теперь о том, зачем нужен TByteArray... Мне необходимо сначала сформировать комманду, т.е. склеить код и данные. Передавать я могу только всё сразу. С приёмом та же ситуация. На ранних этапах комманды вообще склеивались в кучу. Это я заборол настройками сокета. Соображаю дальше...


 
Сергей М. ©   (2007-10-16 08:14) [34]


> Мне необходимо сначала сформировать комманду, т.е. склеить
> код и данные


И ради этого нужно использовать именно TByteArray ?
Мда ..

Ты осознаешь, что из передаваемых тобой при этом 32кб данных лишь 1кб несет полезную информационную нагрузку, а остальные 31кб бестолково "гуляют" по сети ?


> На ранних этапах комманды вообще склеивались в кучу. Это
> я заборол настройками сокета


Это какими же ?
Уж не TCP_NODELAY и SO_RCVBUF/SO_SNDBUF ли ?


 
31512   (2007-10-16 09:08) [35]


> Сергей М. ©   (16.10.07 08:14) [34]

Вот значит... Отправку данных поправил. Отсылается ровно столько, сколько нужно. Ни байтом больше. Осталось разобраться с приёмом ответов от сервера. Быстрого решения пока не вижу.


 
Сергей М. ©   (2007-10-16 09:28) [36]


> Отправку данных поправил. Отсылается ровно столько, сколько
> нужно. Ни байтом больше


Показывай код ..


 
31512   (2007-10-16 09:40) [37]

Ок. Только во загвоздка. Поскольку ничего переписывать нельзя (нет времени) там мягко говоря через одно место сделано, но за-то быстро. И тесты пока успешные.

function TConnection.SendCommand(Command: TCommand; Delay : integer = 0) : TAnswer;
var
PBuffer : Pointer; <--- вот
Buffer : TByteArray;
i, Len : integer;
F : TextFile;
Value : byte;
begin
if not Connected then
 if not TryToConnect then raise ESeparatorConnectException.CreateFmt("При попытке отсылки комманды "%s""#13"cоединение с %s:%d установить не удалось", [Command.Name, IPAddress, Port]);
if Connected then
begin
   try
    Len := Command.BufferLength + 1;
    PBuffer := AllocMem(Len);
    Buffer[0] := Command.Code;
    if Command.BufferLength > 0 then
     for i:=1 to Command.BufferLength do
     begin
       Value := Command.Buffer^[i - 1];
       Buffer[i] := Value;
     end;
    Move(Buffer, PBuffer^, Len);<--- вот
    //FClientSocket.SendBuf(Buffer, Len);
    FClientSocket.SendBuf(PBuffer^, Len);<--- вот
    PBuffer := nil;
    if Delay>0 then Sleep(Delay);
  except on E : Exception do
    begin
     try
      AssignFile(F, "errors\core_log.txt");
      if FileExists("errors\core_log.txt") then Reset(F) else Rewrite(F);
      Append(F);
      WriteLn(F, Format("[%s] TConnection.SendCommand"#13" Комманда:"#13"   Код: %d"#13"   Наименование: %s"#13"   Длина поля данных %d байт"#13"Ошибка: %s", [DateTimeToStr(Now), Command.Code, Command.Name, Command.BufferLength, E.Message]));
      Flush(F);
     finally
      CloseFile(F);
     end;
    end;
  end;
  Result := GetAnswer(Command);
end;
end;

Я прекрасно отдаю себе отчёт в том что это неправильно, но по-моему, то что хотелось. Отсылается ровно столько сколько нужно. Оптимизация будет чуть позже.


 
31512   (2007-10-16 09:42) [38]

Теперь нужно правильно организовать приём данных. Нужен совет.


 
Сергей М. ©   (2007-10-16 09:48) [39]


> Buffer : TByteArray;


И опять TByteArray !

Зачем ?????


> FClientSocket.SendBuf(PBuffer^, Len);<--- вот


Ты вообще [11] читал ?)


 
31512   (2007-10-16 09:59) [40]


> Сергей М. ©   (16.10.07 09:48) [39]

Погоди. Во-первых Buffer - промежуточное явление.
Из него я копирую только то, что мне нужно (не весь массив) в PBuffer.
Len всегда нужная, полная длина пакета комманды. Под PBuffer всегда выделяется столько, сколько нужно. Обращаю внимание: по-хорошему нужно переписать эту часть заново. Но на это нет времени.
Скорее всего я просто не понимаю, что ты хочешь мне сказать. Разве внесённые изменения не обеспечивают передачу только того, что нужно?



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

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

Наверх




Память: 0.59 MB
Время: 0.025 c
2-1193162827
MAXHo
2007-10-23 22:07
2007.11.18
В чем может быть проблема?


15-1192551893
Win
2007-10-16 20:24
2007.11.18
WinXP


15-1192060491
Dmitry S
2007-10-11 03:54
2007.11.18
Нужен ли вопросительный знак


5-1163327689
Poisent
2006-11-12 13:34
2007.11.18
Помогите с редактором свойств.


15-1192070591
Slider007
2007-10-11 06:43
2007.11.18
С днем рождения ! 11 октября 2007 четверг