Как в таком разбираться? В том, что процесс выдает наружу, невозможно почти ничего увидеть, кроме того, что есть разница между двумя запусками - чтобы понять, почему, надо понять, где эта разница возникает во время обработки данных в коде (которого, кстати, много, весь написан не мной, и если основные модули верхнего уровня я хорошо понимаю, есть также и библиотеки, о которых я почти ничего не знаю). Почти невозможно выводить какую-то полезную информацию в логи во время работы процесса, потому что для того, чтобы быть полезной для подробного анализа, ей нужно быть такой огромной по размерам, что с ней не справиться никак. Ведь во время обработки конкретных данных неизвестно, что именно на них в конце концов будет расхождение с другим запуском - это во многой степени случайно - так что пришлось бы писать что-то обо всех, а это нереально. Далее, запустить тот же процесс на гораздо меньшей части исходных данных тоже не получается - если ее как следует уменьшить, чтобы легко было анализировать происходящее, то эффект исчезает.
Я поступил так. Во-первых, все же уменьшил размер исходных данных настолько, чтобы эффект все еще всегда происходил и было довольно много примеров его, но, с другой стороны, один запуск всего процесса начал занимать не X времени, а Y времени, где Y намного меньше X. Во-вторых, вставил код, которых в определенные моменты работы процесса берет релевантные внутренние структуры, сериализует их в массив байтов, вычисляет его хэш, и уже только одно это хэш-значение пишет в некие лог-файлы, которые я потом могу проанализировать (эти файлы тоже оказываются огромными, но уже не гигантскими). Это позволило мне сделать два запуска и после этого убедиться, что в такой-то момент работы в них все еще было идентично... здесь тоже... а вот тут уже расхождение; ага, что у нас происходит между "здесь" и "тут"?
После нескольких сеансов такого сужения определился в общих чертах источник бага, и после этого уже просто внимательное изучение чужого для меня кода помогло найти конкретную проблему. Которая заключалась, кстати, в следующем. Определенная структура данных при создании не обнуляла все свои многочисленные поля, кроме одного: поля-сторожа, гарантирующего разумность всех остальных полей, и по умолчанию равного false. Только когда все остальное заполняется разумными данными, а не мусором, в него ставят true. Теперь представьте себе сочетание двух багов: одного, который в определенных редких обстоятельствах не обрабатывал эту структуру вообще, хотя должен был; и другого, который при обращении к структуре на предмет вытащить из нее полезные данные не проверял значение поля-сторожа... вот тебе, бабушка, и недетерминизм.
Что мне не нравится во всем этом - это то, что такие занятия - нахождение сложных багов в слабознакомом мне коде - мне слишком нравится (ну, если код не безобразный, конечно). Я получаю наслаждение от самого процесса быстрого вникания в незнакомый мне код, перемалывания его концепций и допущений у себя в голове, методично-шерлокхолмсовского вычленения того места, где есть проблема, и отбрасывания тех мест, где нет, погони за багом прыжками по болотным кочкам, с отладчиком наперевес (не в этом случае, но вообще говоря). Это дуэль - даже не с самим багом, который в итоге всегда незначителен, а с хитро спрятавшей его суть окружающей реальностью, дуэль с целью заставить ее объяснить, в чем дело. Мне слишком нравится побеждать в этой дуэли - и этот факт мне не нравится; лучше бы я с большим рвением стремился писать нужное новое свое, чем методично анализировать уже существующее. Конечно, преувеличивать проблему тоже не надо - я очень люблю и свое писать, и пишу; да и баг, который я таким образом нашел, вполне мог бы иначе натворить много чего нехорошего. Это вопрос баланса; но существующий баланс меня не очень устраивает, и я намереваюсь его несколько сдвинуть.
← Ctrl ← Alt
Ctrl → Alt →
← Ctrl ← Alt
Ctrl → Alt →