Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2007.11.18;
Скачать: [xml.tar.bz2];

Вниз

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

 
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;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.58 MB
Время: 0.043 c
2-1193663074
Сергей Ю
2007-10-29 16:04
2007.11.18
Помогите составить запрос с условием по времени


15-1192633707
Slider007
2007-10-17 19:08
2007.11.18
С днем рождения ! 17 октября 2007 среда


15-1192696869
Shlomo
2007-10-18 12:41
2007.11.18
С D7 проект перешёл в D2006 и стал требовать DsnDBCst.pas/dcu – у


15-1192166354
oxffff
2007-10-12 09:19
2007.11.18
Чудеса компилятора Delphi. Баг?


2-1193036085
engine
2007-10-22 10:54
2007.11.18
Проверьте на глючность





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский