QWebEngineCallback - еще один способ изощренного отстрела ноги
В C++
имеется изрядное количество элегантных и не очень способов отстрела ног. Представляю неочевидный (на первый взгляд) и довольно интересный способ, который всплыл на проекте во время тестирования.
Имеется гибридное Qt
приложение, задача довольно экстравагантна: вызываем функцию в js
, в callback
получаем результат и смотрим, что там получилось. В общем, как обычно, внедряли новый функционал и тестили что получилось.
Внезапно начали падать на одном тестовом стенде.
На поиск источника ошибки с момента первой регистрации проблемы ушла почти неделя пассивных поисков. “Упало в недрах QWebEngine при обработке отложенных событий. Воспроизвести не удалось.” - так удавалось отмазываться от глубоких копаний в проблеме, пока случаи падений были единичными.
Но вскоре проблема участилась. Краш-репорты не давали никаких конкретных намеков: в модуль Qt5WebEngineCore
попало событие, у которого QEventPrivate * d = nullptr
. Откуда это событие пошло выяснить не удалось.
Содержимое QEvent * e
из дампа:
- e 0x203cd270 {d=0x00000000 <NULL> t=1000 posted=0 ...} QEvent * {Qt5WebEngineCore.dll!QEvent}
+ [QEvent] {d=0x00000000 <NULL> t=1000 posted=0 ...} Qt5WebEngineCore.dll!QEvent
+ __vfptr 0x03e3f4f8 {Qt5WebEngineCore.dll!const QEvent::`local vftable'} {0x019ad400 {Qt5WebEngineCore.dll!QEvent::`scalar deleting destructor'(unsigned int)}} void * *
d 0x00000000 <NULL> QEventPrivate *
t 1000 unsigned short
posted 0 unsigned short
spont 0 unsigned short
m_accept 1 unsigned short
reserved 1434 unsigned short
Что это, как оно тут оказалось? Мы такого не писали!
С трудом, нам удалось локализовать участок кода, который приводил к проблеме. Он обладал изюминкой: от результата выполнения js
зависела обработка некого event'a
:
class WebView: public QWebEngineView {
...
bool event(QEvent * event){
view->page()->runJavaScript("somePowerfullFunc(someData)", [this, event](const QVariant & result){
if(result.toBool()){
event->accepted();
} else {
event->ignored();
}
});
}
...
};
Работаем с event
‘ом, замечательно. А кто сказал, что он еще валиден? Кто знает, что с ним успело произойти, пока у нас не выполнился callback
?
Важный момент тут в том, что в дебаге все работало замечательно. А релиз проверит кто-нибудь другой нам надо фичи клепать, а не тестировать разные конфигурации запуска. Но в релизе мы крашились через раз в недрах QWebEngine
и ни одного намека на этот callback
в стеке естественно не было.
У нас же есть ревью! Почему это не увидели? Все просто. Потому что запушили большой кусок кода, пока смотрели, глаз замылился. Да и вообще, кто же знал, что так делать нельзя?
Это еще одно подтверждение тому, что после любых изменений (даже самых незначительных) может случиться деградация функционала в совершенно неожиданных местах. Причем, выявить источник проблемы, чем дальше от изменения, тем сложнее.