Partilhar via


Injetando código dinamicamente com a API de depuração

Esta seção descreve a injeção de código dinâmico usando o common language runtime (CLR) API de depuração. Injeção de código dinâmico executa uma função que não estava presente no original arquivo executável portável (PE). Por exemplo, você pode usar a injeção de código dinâmico para executar uma expressão na imediata janela enquanto você está depurando no ambiente de desenvolvimento integrado (IDE) do Visual Studio da Microsoft. O CLR seqüestros de um thread ativo para executar o código. O depurador pode solicitar o CLR para executar ou congelar os segmentos restantes. Porque a injeção de código dinâmico é baseada na avaliação da função, você pode depurar uma função dinamicamente injetada, como se fosse código normal. Ou seja, você pode chamar todos os serviços de depuração padrão como, por exemplo, definir pontos de interrupção, revisão, e assim por diante para o código injetado dinamicamente.

Um depurador deve fazer o seguinte para dinamicamente injetar código:

  • Componha uma função que executará o código dinâmico.

  • Injete o código o ser depurado usando o Edit and Continue.

  • Execute o código, repetidamente, se desejar, chamando a função composta.

As subseções a seguir descrevem essas etapas em detalhes.

A função de composição

  1. Calcule a assinatura da função que executará o código dinâmico. Para fazer isso, acrescente a assinatura para as variáveis locais para a assinatura do método que está sendo executada no quadro folha. Uma assinatura que é construída dessa maneira não requer uma computação de subconjunto mínimo de variáveis usados. O runtime irá ignorar as variáveis não utilizadas. Consulte "Obtendo a assinatura dos metadados" neste tópico para obter mais informações.

    Todos os argumentos para a função devem ser declarados estar ByRef. Isso permitirá a avaliação da função propagar as alterações para as variáveis da função injetada no quadro folha o ser depurado.

    Algumas variáveis não esteja no escopo quando o código dinâmico é executado. Em tais situações, você deve passar um null de referência. Se você faz referência a uma variável de fora do escopo, um NullReferenceException será lançada. Quando isso ocorre, o depurador pode concluir a avaliação da função chamando o ICorDebugManagedCallback::EvalException retorno de chamada.

  2. Escolha um nome exclusivo para a função. Você deve prefixar o nome da função com a seqüência de caracteres "_ Hidden:" para que o depurador irá impedir que um usuário a função de navegação. Quando essa função é adicionada, um sinalizador será definido que indica que o nome da função é especial.

Injetando código

  1. O depurador deve perguntar o compilador para criar uma função cujo corpo é o código que será injetado dinamicamente.

  2. O depurador computa um delta arquivo executável portável (PE). Para fazer isso, as chamadas do depurador do ICorDebugModule::GetEditAndContinueSnapshot método para obter um ICorDebugEditAndContinueSnapshot objeto. As chamadas do depurador ICorDebugEditAndContinueSnapshot métodos para criar a imagem de delta PE e ICorDebugController::CommitChanges para instalar o delta PE na imagem em execução.

    A função dinamicamente injetada deve ser colocada no mesmo nível de visibilidade do quadro folha na qual ele será executado. Se o quadro de folha é um método de instância, a função dinamicamente injetada também deve ser um método de instância na mesma classe. Se o quadro de folha é um método estático, a função dinamicamente injetada também deve ser um método estático. Métodos globais são métodos estáticos que pertencem a uma determinada classe.

    Observação

    A função existirá o depurado mesmo após o processo de injeção de código dinâmico seja concluído.Isso permite que o código injetado anteriormente ser reavaliadas repetidamente sem recompor a função e inserir o código novamente.Portanto, as etapas descritas nesta seção e na seção anterior podem ser ignoradas para o código injetado anteriormente.

Executar o código injetado

  1. Obter o valor (um ICorDebugValue objeto) de cada variável na assinatura usando as rotinas de inspeção de depuração. Você pode usar o ICorDebugThread::GetActiveFrame ou o ICorDebugChain::GetActiveFrame método para obter o quadro de folha, e QueryInterface para ICorDebugILFrame. Chamar o ICorDebugILFrame::EnumerateLocalVariables, ICorDebugILFrame::EnumerateArguments, ICorDebugILFrame::GetLocalVariable, ou ICorDebugILFrame::GetArgument método para obter as variáveis reais.

    Observação

    Se o depurador é anexado a um elemento a ser depurado que não têm o conjunto CORDBG_ENABLE (ou seja, um debuggee não foram Coletando informações de depuração), o depurador não conseguirá obter um ICorDebugILFrame o objeto e, portanto, não será capaz de coletar os valores para a avaliação da função.

    Não é importante se os objetos que são os argumentos da função dinamicamente injetados referência cancelados sem rigidez ou fortemente. Quando a função é avaliada, o runtime irá seqüestrar o thread no qual a injeção ocorreu. Isso deixará o quadro folha original e todas as referências fortes originais na pilha. No entanto, se todas as referências a ser depurado são fracas, executando a injeção de código dinâmico pode disparar uma coleta de lixo que pode fazer com que o objeto seja coletado ao lixo.

  2. Use o ICorDebugThread::CreateEval método para criar um ICorDebugEval objeto. ICorDebugEvalFornece métodos para avaliar uma função. Chame um desses métodos. Um método como ICorDebugEval::CallFunction configura-se apenas a avaliação da função. O depurador deve chamar ICorDebugController::Continue para executar o depurado e avalia a função. Quando a avaliação estiver concluída, os serviços de depuração chamará o ICorDebugManagedCallback::EvalComplete ou ICorDebugManagedCallback::EvalException método para notificar o depurador sobre a avaliação da função.

    Se a avaliação da função retorna um objeto, o objeto será altamente referenciado.

    Se o código injetado dinamicamente tenta cancelar um null que é passada para a função que encapsula o código, o CLR, depuração de serviços de referência chamará ICorDebugManagedCallback::EvalException. Em resposta, o depurador pode notificar o usuário que ele não é possível avaliar o código injetado.

    Observe que o ICorDebugEval::CallFunction método não virtuais despachos; Use ICorDebugObjectValue::GetVirtualMethod em vez disso, se você quiser despachos virtuais.

    Se o depurado é multithreaded e o depurador não deseja que outros threads em execução, o depurador deve chamar ICorDebugController::SetAllThreadsDebugState e definir o estado de todos os segmentos para THREAD_SUSPEND, exceto para o segmento que é usado para a avaliação da função. Essa configuração pode causar um deadlock, dependendo do que faz o código injetado dinamicamente.

Problemas diversos

  • Porque.NET Framework security é determinada pelas diretivas de contexto, injeção de código dinâmico irá operar com as mesmas permissões de segurança e recursos como o quadro de folha, a menos que você altere especificamente as configurações de segurança.

  • Podem ser adicionadas dinamicamente injetadas funções sempre que o recurso Edit and Continue permite funções a serem adicionados. Uma escolha lógica é adicioná-los para o quadro de folha.

  • Não há nenhum limites no número de campos, métodos de instância ou métodos estáticos que podem ser adicionados a uma classe usando as operações de edição e continuação. A quantidade máxima de dados estáticos permitidos é predefinida e atualmente limitada a 1 MB por módulo.

  • Não-locais goto instruções não são permitidas no código injetado dinamicamente.

Obtendo a assinatura dos metadados

Obtendo um repositório de metadados

Localizando o método usando IMetaDataImport

Construindo a assinatura de função

O formato da assinatura é documentado em "Tipo e assinatura codificação nos metadados" especificação e leva a precedência sobre as informações nesta visão geral.

A "declaração de método" a seção na especificação descreve o formato para a assinatura do método. O formato é um único byte para a convenção de chamada, seguido por um único byte para a contagem de argumentos, seguido de uma lista de tipos. Cada tipo pode ter um tamanho diferente. Se um argumento variável convenção de chamada é especificada, a contagem de argumentos será o número total de argumentos, ou seja, fixo argumentos além de argumentos variáveis. Um ELEMENT_TYPE_SENTINEL byte que marca onde começam a final argumentos fixos e os argumentos de variáveis.

"Assinaturas autônomas" a seção na especificação descreve o formato de assinaturas de locais. Assinaturas autônomas não usam um convenção de chamada de argumento variável.

Depois de obter a assinatura do método e a assinatura local, o depurador deve alocar espaço para uma nova assinatura. O depurador deve então iterar a assinatura do método. Para cada tipo, deve colocar o ELEMENT_TYPE_BYREF bytes seguido pelo tipo na nova assinatura. O processo deve ser repetido até que a assinatura de método end ou marcados de um tipo de ELEMENT_TYPE_SENTINEL for atingido. Em seguida, o depurador deve copiar os tipos de assinatura local e marcar cada tipo de ELEMENT_TYPE_BYREF. Se a assinatura do método tem um convenção de chamada de argumento variável, o depurador deve copiar esses tipos e marcá-las ELEMENT_TYPE_BYREF. Finalmente, o depurador deve atualizar a contagem de argumentos.

Consulte também

Conceitos

Visão geral de depuração do CLR

Outros recursos

Depuração (referência de API não gerenciada)