ОК, я только что нашел часть исходного кода, которая подтверждает мои интуитивные ощущения из комментария.
Вот вина Git за то, когда он был добавлен: https://github.com/dlang/druntime/blame/bc940316b4cd7cf6a76e34b7396de2003867fbef/src/core/runtime.d#L756
Увы,сообщение коммита не суперинформативно, но сам код вместе с моей памятью очень меня убедил.
Так что это файл core/runtime.d
в библиотеке druntime
.На момент написания этой статьи она находилась в строке 756
enum CALL_INSTRUCTION_SIZE = 1; // it may not be 1 but it is good enough to get
// in CALL instruction address range for backtrace
callstack[numframes++] = *(stackPtr + 1) - CALL_INSTRUCTION_SIZE;
Обратите внимание, что переменная callstack
создает копию текущих вызовов при возникновении исключения.При запросе на фактическую запись принтер трассировки будет смотреть на этот массив, чтобы определить, что писать.(Видите, ДЕЙСТВИТЕЛЬНО МЕДЛЕННО искать отладочную информацию для печати номеров файлов / строк и имен функций, поэтому он делает это только тогда, когда это необходимо, чтобы сохранить нормальное использование исключений - когда оно генерируется и перехватывается позже - быстрее.)
Во всяком случае, я помню, когда использовалась обратная трассировка для печати неправильной строки.Это напечатало бы строку кода, содержащую следующую инструкцию - которая может быть довольно далеко в исходном тексте от фактического утверждения assert / throw, делая печать менее полезной.Если вы посмотрите на эту ссылку git blame, вы увидите старый код, используемый для буквального копирования адресов прямо из стека.
Инструкция call
работает, помещая адрес возврата в стек, затемпереход на адрес подпрограммы.Обратный адрес сразу же после инструкции вызова, поэтому, когда ЦП вернется туда, он не будет снова запускать вызов.Вот почему старый код будет показывать неправильный номер строки, неправильно возлагая вину на следующую инструкцию:
Новый код немного перематывает этот адрес, чтобы вернуть его к самой инструкции вызова - таким образом, помещая напечатанный кодФункция на линии, где он принадлежит.Но на x86 есть несколько разных инструкций вызова, и я даже не уверен, что можно правильно перемотать назад - вы можете определить реальный размер инструкции только по коду операции иВы знаете только, где находится код операции, если знаете размер инструкции или читаете код в прямой последовательности, как это делает сам процессор.Более того, на других процессорных архитектурах размер будет другим.
Как и в комментарии в этой строке, мы не должны быть идеальными.Цель этого обратного следа - просто заставить пользователя смотреть в нужном месте.Отладочная информация использует своего рода ограничивающий прямоугольник - если вы находитесь по начальному адресу этой функции или строки источника или после него, но еще не по начальному адресу следующей функции / строки, она считает вас там.Он не знает и не заботится о дробных строках кода.
Таким образом, он значительно упрощает реализацию, просто предполагая, что размер равен 1 - достаточно хорош, чтобы вернуть его в эту границу.
Бьюсь об заклад, GDB делает что-то аналогично внутри, просто его принтер скрывает это, показывая адрес возврата из стека непосредственно в его обратной трассировке.(Кстати, забавный совет: передайте --DRT-trapExceptions=no
аргументам командной строки вашей программы при запуске ее внутри GDB. Затем она будет перехватывать точку выброса при работающей программе вместо того, чтобы печатать сообщение и говорить, что программа завершилась с кодом 1!)
Код печати druntime также может +1 возвращаться к нему перед печатью, чтобы скрыть этот внутренний взлом реализации ... но ме.Обратный адрес также не там, где на самом деле был звонок, вам нужно посмотреть выше в вашем дизассемблере.И даже GDB на самом деле не показывает адрес вызова (по крайней мере, не моя старая версия этого, может быть, новые показывают).Но было бы неплохо, если бы в разборке было значение для разборки, независимо от ... Если вы хотите сделать пиар в пьяное время, я бы поддержал вас в этом (заметьте, у меня нет полномочий, но я могу помочь с комментариями).
Но это, по крайней мере, однозначно объясняет статус-кво.