Как отследить нарушение доступа «по адресу 00000000»

Это то, что вы ищете:

- hosts: localhost
  tags: s21
  gather_facts: no
  vars:
    images:
      - foo
      - bar
  tasks:
    - shell: "echo result-{{item}}"
      register: "r"
      with_items: "{{images}}"

    - debug: var=r

    - debug: msg="item.item={{item.item}}, item.stdout={{item.stdout}}, item.changed={{item.changed}}"
      with_items: "{{r.results}}"

    - debug: msg="Gets printed only if this item changed - {{item}}"
      when: "{{item.changed == true}}"
      with_items: "{{r.results}}"

Источник: Регистрировать переменные в цикле in_items в Ansible playbook

29
задан ObiWanKenobi 10 February 2010 в 13:21
поделиться

8 ответов

Нарушение доступа в любом месте рядом с адресом '00000000' указывает на доступ по нулевому указателю. Вы используете что-то до того, как оно когда-либо было создано, скорее всего, или после того, как оно было FreeAndNil () 'd.

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

MadExcept позволяет довольно легко отслеживать эти вещи и бесплатен для некоммерческого использования. (На самом деле, лицензия на коммерческое использование также довольно недорогая и стоит своих денег.)

29
ответ дан 28 November 2019 в 01:03
поделиться

Принятый ответ не раскрывает всей истории.

Да, всякий раз, когда вы видите нули, используется указатель NULL . Это потому, что NULL равно по определению ноль. Так что вызов нуля NULL может мало что сказать.

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

В сообщении говорится, что адрес NULL пытался прочитать NULL . Так что это значит? В частности, как адрес считывается сам себя?

Обычно мы думаем об инструкциях при чтении адреса и записи из памяти по определенным адресам. Знание этого позволяет нам проанализировать сообщение об ошибке. Сообщение пытается сформулировать, что инструкция по адресу NULL пыталась прочитать NULL .

Конечно, по адресу NULL нет инструкции, поэтому мы считаем NULL особенным в нашем коде. Но каждую инструкцию можно рассматривать как начало с попытки прочитать саму себя. Если регистр CPU EIP находится по адресу NULL , то CPU попытается прочитать код операции для инструкции с адреса 0x00000000 ( NULL ]). Эта попытка прочитать NULL завершится ошибкой и сгенерирует полученное вами сообщение.

В отладчике обратите внимание, что EIP равняется 0x00000000, когда вы получаете это сообщение. Это подтверждает описание, которое я вам дал.

Тогда возникает вопрос: «Почему моя программа пытается выполнить адрес NULL ».«На ум приходят три возможности:

  • Вы пытаетесь вызвать функцию с помощью указателя функции, который вы объявили, присвоили NULL , никогда не инициализировались в противном случае и выполняете разыменование.
  • ] Точно так же вы можете вызывать «абстрактный» метод C ++, который имеет запись NULL в vtable объекта. Они создаются в вашем коде с синтаксисом virtual function_name () = 0 .
  • В вашем коде буфер стека был переполнен при записи нулей. Нули были записаны за пределами буфера стека, по сохраненному адресу возврата. Когда функция позже выполняет свое ret инструкция, значение 0x00000000 ( NULL ) загружается из места перезаписи памяти. Этот тип ошибки, переполнение стека, является эпонимом нашего форума.

Поскольку вы упомянули, что вызываете третью - партийная библиотека, я отмечу, что это может быть ситуация, когда библиотека ожидает, что вы использовать указатель на функцию, отличный от NULL , в качестве входных данных для некоторого API. Иногда их называют функциями обратного вызова.

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

32
ответ дан 28 November 2019 в 01:03
поделиться

Вы начинаете искать около того кода, который, как вы знаете, запускал, и прекращаете поиск, когда дойдете до кода, который знаете, не запускал.

Вероятно, вы ищете место, где ваша программа вызывает функцию через указатель на функцию, но этот указатель имеет значение NULL.

Также возможно повреждение стека. Возможно, вы перезаписали адрес возврата функции нулем, и исключение возникнет в конце функции. Проверьте возможные переполнения буфера и, если вы вызываете какие-либо функции DLL, убедитесь, что вы использовали правильное соглашение о вызовах и количество параметров.

Это не обычный случай использования нулевого указателя, такого как ссылка на неназначенный объект или PChar. В таких случаях у вас будет ненулевое значение "по адресу x ". Поскольку инструкция произошла по адресу 0, вы знаете, что указатель инструкции ЦП не указывал ни на одну допустимую инструкцию. Вот почему отладчик не может показать вам, какая строка кода вызвала проблему - там нет строки кода. Вам нужно найти его, найдя код, ведущий к месту, где ЦП перешел на недопустимый адрес.

Стек вызовов может остаться нетронутым, что, по крайней мере, должно приблизить вас к вашей цели. Однако, если у вас есть повреждение стека, вы, возможно, не сможете доверять стеку вызовов.

7
ответ дан 28 November 2019 в 01:03
поделиться

Когда я сталкиваюсь с этой проблемой, я обычно начинаю искать те места, где я использую FreeAndNil () или просто xxx: = NIL; переменные и код после этого.

Когда ничто другое не помогло, я добавил функцию Log () для вывода сообщений из различных подозрительных мест во время выполнения, а затем просмотрел этот журнал, чтобы отследить, где в коде происходит нарушение прав доступа.

Есть, конечно, много более элегантных решений для отслеживания этих нарушений, но если в вашем распоряжении их нет, старомодный метод проб и ошибок подойдет.

2
ответ дан 28 November 2019 в 01:03
поделиться

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

По моему опыту, самый простой способ отследить это - запустить программу с отладчиком и сделать трассировку стека.

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

Взгляните на Stack Tracer, который может помочь вам улучшить отладку.

1
ответ дан 28 November 2019 в 01:03
поделиться

Используйте MadExcept. Или JclDebug.

1
ответ дан 28 November 2019 в 01:03
поделиться

Я поддерживаю madExcept и подобные инструменты, такие как Eurekalog, но я думаю, что вы можете пройти хороший путь и с FastMM. При включенном режиме полной отладки он должен дать вам некоторые подсказки о том, что не так.

В любом случае, даже если Delphi использует FastMM по умолчанию, стоит приобрести полный FastMM для дополнительного контроля над логированием.

1
ответ дан 28 November 2019 в 01:03
поделиться

Если вы получаете 'Access violation at address 00000000.', вы вызываете указатель функции, который не был назначен - возможно, обработчик событий или функцию обратного вызова.

например

type
TTest = class(TForm);
protected
  procedure DoCustomEvent;
public
  property OnCustomEvent : TNotifyEvent read FOnCustomEvent  write FOnCustomEvent;
end;

procedure TTest.DoCustomEvent;
begin
  FOnCustomEvent(Self);  
end;

вместо

procedure TTest.DoCustomEvent;
begin
  if Assigned(FOnCustomEvent) then // need to check event handler is assigned!
    FOnCustomEvent(Self);  
end;

Если ошибка в стороннем компоненте, и вы можете отследить код-нарушитель, используйте пустой обработчик событий, чтобы предотвратить AV.

3
ответ дан 28 November 2019 в 01:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: