• ↓
  • ↑
  • ⇑
 
Записи с темой: борьба с техникой (список заголовков)
23:58 

Парадокс близнецов

Отлаживал программу при помощи отладочных сообщений. То есть, сообщения по ходу выполнения программы пишутся в лог-файл.

Время на часах -- 14:00. Выполняю в программе операцию, которую хотел проверить. Захожу в лог -- а там последняя запись -- 13:52. Что такое?! Как так?! Проделал ещё раз операцию -- то же.

Потом меня стали терзать смутные сомнения. Я отодвинул окошко, загораживающее часы в трее.

13:53.

Работал на целевом компьютере я по удалёнке. И смотрел время по часам основного компа. Часы целевого компа отставали на 8 минут.

@темы: Борьба с техникой, Программирование

23:59 

То лапы ломит, то хвост отваливается

Перевели один из проектов из-под MSVS 2008 под MSVS 2015. Экзешник вырос в два раза и перестал работать под Windows XP! (наше ПО эксплуатируется в т.ч. на машинах с XP)

Потом, правда, оказалось, что есть специальный набор legacy-библиотек.

@темы: Борьба с техникой, Программирование

23:52 

Раскачивание лодки

У нас на работе используется специальная PCI-карточка, которая работает только если в настройках ресурсов выделить строго определённые диапазоны портов и прерывания. Это, конечно, недоработка. А всё потому, что карточка самодельная.

В один день карточка перестала работать, потому что её адреса кто-то занял. Но кто?

Это оказался контроллер шины PCI!

Революционная ситуация, блин. Верхи не могут, низы не хотят.

@темы: Борьба с техникой

23:58 

Лакуна

Смотрю, пакеты от устройства приходят не целиком (это как раз то, из-за чего я делал вчерашние дампы). Конкретно -- отсутствует начало. А иногда вообще ответ не приходит. Причём "недостача" возникает только в начале процесса обмена. Если первый пакет пришёл нормально, все остальные тоже приходят нормально.

Оказалось, я делал вот что: сначала я посылал запрос к устройству, а потом запускал отдельный поток для считывания данных.

Однако поток при старте очищал входной буфер виртуального COM-порта! На всякий случай -- вдруг там мусор какой от предыдущих передач остался?

Если мне везло, то устройство не успевало ответить до очистки буфера, и ошибка не возникала. Поскольку очистка производилась только при старте потока, дальнейшие запросы и ответы воспринимались нормально. Если же мне не везло, то часть ответа (или даже весь ответ) оказывалась стёрта.

@темы: Борьба с техникой, Программирование

23:57 

Дым отечества нам сладок и приятен

Схема, на входе генератор, на выходе -- нагрузка. Пришли ребята из соседнего отдела и попросили осциллограф, чтобы посмотреть сигнал на нагрузке. И меня попросили в комплекте, чтобы я об'яснил, как им пользоваться.

Прихожу, цепляю землю осциллографа на землю (минус) схемы, а плюс (сигнал) осциллографа -- на выход нагрузки. Включаем установку -- запахло палёным. Очень нам повезло, мы подключили осциллограф не напрямую в схему, а через прводок МГТФ 0,2. На нём сгорела изоляция и сам он накалился до красна. Если бы не этот проводок, мы бы заметили неполадку одновременно со сгоранием щупа осциллографа. Там провод толще, поэтому он сгорел бы позже, но без предупреждения. Сжигать щупы осциллографов -- плохо.

Думали, почему начинает накаляться провод ЗЕМЛИ, когда там тока быть не должно -- ничего не придумали. Позвали более опытного специалиста. Он провёл стандартную диагностику и обнаружил причину. Вот блок-схема:


Для начала, выяснилось, что генератор был подключён минусом к плюсу схемы. Это было сделано умышленно. Пока мы не подключали осциллографа, всё было хорошо, т.к. плюс питания схемы был прибит к земле (минусу) генератора, а минус питания болтался где хотел (в смысле потенциала). Получалась схема как бы с отрицательным питанием.

Когда я подключил осциллограф, его земля оказалась соединена с минусом питания схемы -- это правильно. Однако ОКАЗАЛОСЬ! что земля осциллографа соединена с защитной землёй евророзетки, и, таким образом, соединена с минусом (землёй) генератора. То есть, через защитную землю накоротко замыкались плюс и минус питания. Плюс питания -- земля генератора -- земля евророзетки -- земля осциллографа -- минус питания.

Честно говоря, я не знал, что земля осциллографа соединена с защитным заземлением евророзетки. Я думал, она свободно болтается, чтобы можно было мерить напряжение между двумя любыми точками схемы.

@темы: Борьба с техникой, Электроника

23:59 

Вавилонская башня

Микроконтроллер выдает HardFault (аппаратная ошибка). После какой конкретно строчки программы он туда вываливается по техническим причинам выследить невозможно.

Смотрю -- происходит это по адресу 0x1800 0000. Там как раз заканчивается настоящая оперативная память и начинает пустое адресное пространство. Ставлю точку останова по адресу 0x17FF FFFA -- недалеко от конца. Но она не срабатывает. Вывод -- в этот адрес упирается не программа, а данные.

Нахожу в коде следующий текст:

char out_buf[200];
char temp_buf[20];
int x;
int a,b,r[10];
// ---

//sprintf(out_buf,"%d\n",a);
sprintf(temp_buf,"%d\n",b);
strcat(out_buf,temp_buf);
for(x=0;x<10;x++)
{
sprintf(temp_buf,"%d\n",r[x]);//напечатать очередной кусок текста в строку
strcat(out_buf,temp_buf);//добавить к основной результирующей строке
}
//...


Вопрос о том, зачем нужны sprintf в прошивке, где в текстовых данных нет особой необходимости, оставляю за кадром. Так надо.

sprintf печатает в строку, strcat сшивает (конкатенирует) две строки.

Данный кусок выполняется в основном бесконечном цикле прошивки.
Первый вызов sprintf закомментирован. Он попал под раздачу, т.к. отвечал за одну из функций прошивки, которая была больше не нужна. Вместо её удаления, я её закомментировал. И правильно сделал. Благодаря этому я легко смог опознать, в чём же было дело.

Эта закомментированная строка писала в выходной буфер с его начала. А остальные операторы дописывали к его концу. Когда же я строку закомментировал, при каждой следующей итерации новые данные дописывались в конец строки. Которая всё удлинялась и удлинялась. Пока не упёрлась в конец памяти!

Решением в данном случае было дописать после закомментированной строки:

out_buf[0]='\0';

Тогда заполнение стало происходить с нуля каждый раз.

P.S. Только что подумал, что можно оптимизировать, сделав:

sprintf(out_buf+strlen(out_buf),"%d\n",r[x]);

@темы: Программирование, Борьба с техникой

23:59 

Плюс пять мне сделал прокурор

На одном компьютере было сообщение "Ваша копия Windows не является подлинной". Но пользователь сообщил, что никаких ограничений по работе он не заметил, кроме невозможности поставить обои.

Но тут надо было поставить туда обновление из .msu-пакета. И оказалось, что такая винда не позволяет ставить одиночные обновления!

@темы: Борьба с техникой, Программы

23:59 

Вас здесь не стояло

При входе в main() микроконтроллерной программы:
а) МК вылетает в исключение HardFault, если запустить программу на автоматическое выполнение до брейкпоинта.
б) если выполнять пошагово до него же, то всё работает.
в) после каждой строчки начиная с первой выдаётся предупреждение, что указатель стека вне допустимого диапазона.

Я изучил в дизассемблере пролог функции main(), т.е. в промежуток между её началом и первой командой:

int main()
{
// <- тут пролог... его не видно, потому что его делает компилятор.
func1();
//...
return 0;
}


Обнаружил там странную команду:

SUB.W SP, #7306

То есть, вычесть из указателя стека семь тысяч с гаком. А стека было всего две тысячи. То есть, программа не работала потому, что новый указатель стека выходил за пределы стека, о чём и выдавалось предупреждение. Но это ещё не всё -- указатель оказывался В ОБЛАСТИ КОДА и переписывал его данными на ходу. Это МК, тут нету DEP. Этим и был вызван HardFault.

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

@темы: Программирование, Борьба с техникой

23:59 

Бур пронзит небеса

На работе у одного из приборов есть движущаяся часть, на которую установлен датчик положения. Можно следить за положением этой части вдоль одной из осей с точностью до долей микрометра. И никак не удавалось сделать, чтобы часть останавливалась где надо. То недоезд, то переезд. Нам нужна была точность плюс-минус 1 мкм. 1.1 -- уже не катит.

Тогда решили следить за координатой, когда движущаяся честь неподвижна.

ОКАЗАЛОСЬ, что координата меняется! А потом мы поняли, что рядом -- открытое окно.

По-видимому, дело было в термическом расширении и скукоживании деталей (металлических), которое по прикидкам при длине детали около 10 см (а у нас как раз такая была) достигает 1 мкм при колебании температуры на 1 градус.

@темы: Борьба с техникой

23:57 

Выстрелил в воздух, но не попал

Чинил ноутбук. Симптом: не запускается Касперский. Два раза кликаешь по иконке, а он ничего не показывает. Сервис при попытке запуска показывает ошибку.

Думаю, наверное вирус засел.

Открываю сайт Микрософта (а он обычно вирусами блокируется в первую очередь). Открывается.
Открываю сайт Касперского. Пишет: "На сайте проводятся технические работы, зайдите позже".

Подхожу к соседнему компьютеру, открываю сайт Касперского. Открывается.

Ну, думаю, всё понятно. Наверное, вирус подменил DNS, так что на ноутбуке открывается фальшивый сайт Касперского. Проверяю -- адреса одинаковые. Значит, дело хуже. Я сталкивался с вирусом, который подменял драйверы в стеке TCP/IP. Но может быть дело в каком-нибудь плагине для браузера?

На всякий случай на ноутбуке нажимаю F5 -- и тут сайт Касперского открывается на ноутбуке тоже!

То есть, не было никакого мальчика вируса. Я просто случайно попал на момент, когда технические работы закончились. Причём меньше минуты прошло от открытия сайта на ноутбуке и на соседнем компьютере!

@темы: Борьба с техникой

23:59 

Стой там, иди сюда

Посылаю данные через последовательный интерфейс (SPI) из контроллера. Для тестовых целей посылаю по очереди 0xFFFFFF и 0xAAAAAA. По системе:

for(;;)
{
SendData(0xFFFFFF);
Pause(1);//us
SendData(0xAAAAAA);
Pause(1);//us
}


Но на осциллографе вижу какую-то хрень: вроде данные видны, но через несколько тактовых импульсов (а там есть вторая линия, где вместо данных -- тактовые импульсы) происходит сбивка длительности импульса (т.е. они все должны быть одинаковой длины, а очередной импульс оказывается другой длины). И данные совсем не те. А число импульсов... ОДИННАДЦАТЬ (а должно быть 24, я так задал). Вообще ни в какие ворота не лезет.

ОКАЗАЛОСЬ, что блок контроллера, отвечающий за SPI занимается посылкой данных независимо, т.е. как только я данные ему отправил, он сразу возвращает управление. А функция SendData не ждёт, пока завершится передача. А 11 импульсов проходит за время паузы как раз. И блок SPI бросает все дела и начинает посылать следующие данные, недопослав предыдущие.

@темы: Борьба с техникой, Программирование

23:56 

E

Компьютер стал перезагружаться без видимой причины. Дело не в синем экране смерти (проверил по логам) и не в температуре. Компьютер просто... перезагружается. Пока не выяснил, почему.

И вот сижу я себе, что-то читаю, потом встаю и ухожу в другую комнату. И тут вдруг слышу "бип".

Что такое один "бип"? Это успешное прохождение POST. А когда это происходит? При старте компьютера. Если компьютер стартовал, если до этого он уже был включён, значит он перезагрузился.

Это был самый неприятный однократный "бип" в моей жизни.

UPD. По-видимому, дело было в неисправном БП. Я его заменил и перезагрузки прекратились.

@темы: Борьба с техникой

23:36 

Фейл-моногатари [05]

В прошивке контроллера написано выдавать сигнал на выход, а осциллограф этого не видит. Никак не мог понять, в чём дело.

Решил -- контроллер работает на частоте 200 МГц. Возможно, сигнал проскакивает так быстро, что осциллограф просто не срабатывает? (он работает в режиме single sequence: при появлении сигнала он начинает измерение данных, а потом показывает статичную картинку начиная с момента появления сигнала)

Думаю, надо для теста уменьшить частоту. Так, какие у нас ещё есть источники для тактовой частоты? О, тактовый генератор 32 кГц! Его и поставлю!

И заменил строчку:
REGISTERS->PERIPHERAL_CLOCK=9<<24;//основной кварцевый генератор; в 24-28 битах должна быть 9,
//остальные биты 0; << -- логический побитовый сдвиг влево.

На строчку:
REGISTERS->PERIPHERAL_CLOCK=1<<24;//генератор 32 кГц на RC-цепочке

Контроллер после этого прошился, но при попытке выполнить пару строк кода завис. И больше он теперь не прошивается!

@темы: Фейлы, Программирование, Борьба с техникой

23:55 

Зазеркалье

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

Итак, в одном из проектов у нас есть маленький модуль. Он реализует обработку команд на специальном птичьем языке програмирования. Он был придуман давным-давно, ещё до моего прихода в нашу организацию. Этот язык не поддерживает условия, но поддерживает циклы. Перед выполнением циклы "разворачиваются", в итоге получается фиксированная последовательность команд без переходов, которая исполняется последовательно.

То есть, до разворачивания макрос выглядел так:

loop 3
command1
command2
endloop

А после разворачивания так:

nop
command1
command2
nop
nop
command1
command2
nop
nop
command1
command2
nop

По тактическим соображениям команды начала и конца цикла не удалялись, а заменялись на nop (пустая команда).

Циклы поддерживались только одного уровня вложенности, в то время как существовала необходимость сделать циклы минимум вложенности 2. Это задание было выдано мне. Я решил сразу делать цикл вложенности n. Как определять границы циклов -- плёвая задача. В итоге я написал функцию:

void Unfold(CMacros *pMacros, unsigned int nStart, unsigned int nEnd);

Она должна была брать макрос pMacros, извлекать из него заданный набор команд -- от номера nStart до номера nEnd -- повторять их заданное число раз (тут этот момент опущен) и вставлять после первого встречания тела цикла. Там было написано примерно следующее:

unsigned int x;
for(x=nEnd;x>=nStart;x--)
{
pMacros->aCommands->InsertAt(nEnd+1,pMacros->aCommands->GetAt(x));
}

InsertAt -- вставляет в заданную позицию, сдвигая то, что там было (и всё дальнейшее), дальше по массиву.
aCommands -- массив команд.
GetAt -- обращение к массиву по заданному индексу.

Эта штука работала без нареканий 2 года. Но сегодня ружьё выстрелило. Программа падает. Почему? Выход за границы массива в GetAt.

ОКАЗАЛОСЬ, что nStart равен нулю. Два года работы, десятки скриптов. Ни у одного из них начало цикла не было первой командой! Поэтому значение типа unsigned int (переменная x) всегда сравнивалось с числом 1 или больше (очевидно, первая команда имела номер 0, а не 1). И когда оно становилось равно в худшем случае нулю, цикл прекращался. Когда же цикл оказался стоящим первой командой, возникла прикольная ситуация. Число типа unsigned всегда будет больше либо равно нуля! Если из него вычесть 1, оно станет равно 2^32-1 (если int 32 бита). Перенос! И по этому четырёхмиллиардному смещению программа пытается что-то там прочитать.

Я смотрю на это и говорю -- а давайте поменяем тип x на int. Тогда он станет равным -1, а -1>=0 это ложь. Т.к. условие в цикле for проверяется ДО тела, то цикл завершится ещё до того, как -1 попадёт в аргумент GetAt.

Сделали. Но не помогло. Происходил вход в тело цикла! Тогда мы заменили nStart и nEnd тоже на просто int -- и всё заработало!

Что это означает? Что при проверке условия x>=nStart по неизвестной причине int преобразуется к unsigned int, а не наоборот! И -1 при сравнении превращалась в ту же 2^32-1 или может и во что похуже, но это значения не имеет, т.к. оно было типа unsigned int, а любое число этого типа будет заведомо больше либо равно нулю.

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

@темы: Программирование, Борьба с техникой

23:59 

Untitled [451]

Обнаружил странную особенность Яндекс.Карт, провляющуюся ИЗРЕДКА на двух разных компах. При попытке открыть страницу Яндекс.Карты браузер Firefox намертво зависает. После перезагрузки браузера при попытке открыть ту же страницу -- опять зависает. Помогает перезагрузка.

Встречался ли кто-нибудь с этим?

@темы: Борьба с техникой

23:59 

Аппендикс

Мне дали готовый проект для микроконтроллера когда-то. И вот понадобилось добавить туда поддержку SPI (последовательный аппаратный интерфейс для связи с внешними устройствами). Проект на Си, контроллер LPC.

Обычно есть готовые библиотеки для работы с частями контроллера. А у контроллера поддержка SPI была. Но заголовочного файла для SPI почему-то не было. Однако файлы имели имена типа lpc_model_device.h, где model -- модель устройства, а device -- подсистема контроллера. Например, lpc_1111_usart.h -- поддержка USART.

Забиваю в гугл -- lpc_1111_spi.h -- и нахожу библиотеку, из которой были стырены заголовочные файлы проекта, который мне дали. Библиотека от производителя -- NXP. Беру нужные два файла (заголовочный и сишный), качаю, добавляю в проект, пытаюсь собрать.

Вижу сообщение об ошибке:

LPC_SPI_BASE: identifier not found.

Как же так. Открываю "базовый" заголовочный файл, содержащий идентификаторы всех устройств (lpc_1111_cgu.h). Он имеет ту же дату, что и файл в интернете. Но в файле в интернете этот идентификатор есть! А в моём его нет.

Смотрю, что написано в моём:

enum DEVICES
{
LPC_DEV1_BASE=0,
//...
LPC_SSP_BASE,
LPC_I2S_BASE=LPC_SSP_BASE+2,
///...
};

В файле из интернета же после SSP_BASE расположен SPI_BASE. То есть авторы проекта его зачем-то не просто вырезали из готовых исходников, но и даже подправили нумерацию, чтобы не сбилась. То есть, это было сделано сознательно. Зачем -- ещё предстоит выяснить.

@темы: Борьба с техникой, Программирование

23:58 

Размытие текста на TJournal

TJournal -- очередной сайт, который возражает против использования АдБлока. Он поступает иначе, чем остальные. Он делает всю страницу размытой, так что читать невозможно. Видно только об'явление -- отключите адблок или гоните бабки.

Я решил разобраться в механизме размытия.

Первое, что я заметил -- размытый текст можно выделять (!) и копировать в буфер обмена. При этом в Блокнот он копируется неразмытым. Это означало, что текст там реально грузится, а поверх него ничего нету.

Полез в стили.

ОКАЗЫВАЕТСЯ

Для всего блока <html> прописан класс m_blurry (хотя я ожидал, что он будет прописан для одного из div-ов внутри, т.к. некоторые навигационные панели отображались нормально). Если его удалить у этого блока в консоли веб-разработки, текст появится. Но за счёт чего же он работает?

В таблице стилей указано следующее:

filter: blur(5px);

То есть, размытые -- встроенная функция CSS! Вот это да...

@темы: Борьба с техникой

23:59 

День не в счёт, часть 2

Ещё про рекламу. Яндекс.Директ. Оказывается, на каждом об'явлении-баннере у них есть крестик. Жмёшь на крестик -- открывается окошко.

"Почему вы хотите закрыть эту рекламу?"

И закрытый список. Варианта "другое" или "мне не нравится реклама в принципе" -- нету. Пока не выбрал причину, нажать кнопку "скрыть" -- нельзя. Когда выбрал -- можно. Реклама пропадает, вместо этого появляется надпись "Объявление скрыто". Но уже при следующей загрузке страницы оно появляется опять. Другое. При этом если одна фирма дала несколько об'явлений, то скрывается только конкретное.

Тем временем, антиблокировщики становятся всё изощрённее. Допустим, у вас блокировщик рекламы+отключены скрипты. По-умолчанию грузится страница, где текст загорожен сообщением о том, что надо выключить ад-блок и включить скрипты. Если скрипты разрешены, они проверяют -- разрешена реклама или нет. Если разрешена, загородку убирают. Хотя её можно убрать и вручную. При помощи консоли разработчика.

@темы: Борьба с техникой

23:57 

Program Data Updater: отрубленный палец для проверки отпечатка

Ссылки носят справочный характер, для понимания содержимого поста их читать не обязательно.
Пост №1: zhz00.diary.ru/p208127808.htm
Пост №2: zhz00.diary.ru/p210620614.htm

А сегодня типа, FINALE.

Винда 8.1 (2012 R2). У меня было задание в Task Scheduler -- Program Data Updater. При запуске оно жрало 100% процессора и не давало нормально работать. Снимал задачу вручную. Хотя я его и обнаружил, я не мог понять, почему оно запускается, поскольку в поле "условие запуска" было пусто. Кроме того, я не видел истории запусков.

В посте №1 я обнаружил, откуда оно берётся.
В посте №2 я поставил ведение истории и обнаружил, что пока Таск Шедулер запущен, задача не запускается.

Тогда я выключил Таск Шедулер и о чудо! Никаких пятен! На второй день задача запустилась. Я открыл список задач, но там оказалось, что не только каждый час, но каждые 10 минут запускается десяток задач, которые делают неясные вещи -- понять, которая из них моя, я не мог. При этом ниже в окне "активные задачи" (зона 2, см. далее) некоторых из них нет. Вот, кстати, это окно:


1 -- зона запускавшихся в недавнее время задач.
2 -- активные задачи, т.е. те, на которые стоят условия запуска.
3 -- дерево, в котором зашиты все задачи; они спрятаны в папках.

За время, прошедшее с поста №1 я уже забыл, что моя задача называлась Program Data Updater (а это телеметрия). Но когда я увидел её в списке запускавшихся задач (зона 1) -- вспомнил. Нашёл её, открыл свойства. Там же я обнаружил историю запусков, которую не смог найти в посте №2. Деталь тут такая, что от списка запускавшихся задач (зона 1) перейти к их свойствам нельзя. Надо догадаться, в какой папке лежит задача в дереве слева (зона 3). Это очень любопытно, поскольку если задача активная (в зоне 2), то по двойному щелчку открываются её свойства, а вот если она запускалась (есть в зоне 1), но в списке активных (зоне 2) её нет, обнаружить её будет не так-то просто -- придётся перерыть всё дерево (в зоне 3).

ОКАЗАЛОСЬ

что в качестве причин запуска задачи указано "запущено пользователем"!!!!!!!!!!!!!!!!! Вот это номер! Думаю, излишним будет указывать, что я эту задачу не запускал (правда, она должна была запускаться от лица системы).

Удалять задачу я не хотел, чтобы ничего не сломалось.

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

An error has occurred for task ProgramDataUpdater. Error message: The following error was reported: 2147750680.

Поверхностное гугление ничего не дало. Тогда я заменил в настройках rundll32.exe на notepad.exe . Прокатило!

Страшно подумать -- сколько ещё таких жутких задач на моём компе. При этом они не жрут процессор, а значит я их не могу заметить.

@темы: Борьба с техникой

23:59 

Закладка на дно

Есть у меня на компьютере (8.1) некая программа, которая изредка запускается и занимает весь процессор (запускается через rundll32.exe). В процессе выяснения того, откуда она берётся, я обнаружил, что она загружается через Task Scheduler. Я хотел посмотреть, как часто фактически это происходит, но оказалось, что история запуска не ведётся, пока её не включишь.

Включил. И забыл на полгода. Когда программа запускалась -- вырубал вручную.

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

Тогда я решил оставить Task Scheduler открытым, чтобы как только программа запустится, сразу посмотреть, Какая задача только что запустилась -- и вычислить её. Типа, засада.

И вот уже 2 недели та программа активности не проявляет, хотя раньше запускалась раз в 2-3 дня.

@темы: Борьба с техникой

Untitled

главная