Resumo

  • O RFC 909, de julho de 1984, descreveu um protocolo de aplicação para carregar, descarregar e depurar à distância uma máquina como um nó de comutação ou gateway. Os autores observaram que um breakpoint que parasse o alvo enquanto aguardava novas instruções do host poderia impedi-lo de atender às exigências em tempo real de sua aplicação.
  • O projeto oferecia três níveis cumulativos de implementação. No nível completo, o host poderia instalar listas condicionais no alvo, que as executaria ao disparar um breakpoint. A viagem da instrução pela rede deixava de ser obrigatória naquele instante, mas o código e o estado do depurador passavam a consumir recursos da própria máquina observada.

Diagnóstico com custo para quem usa a rede

Christopher Welles e Walter Milliken deram ao Loader Debugger Protocol, ou LDP, uma missão ambiciosa: um host poderia carregar programas, extrair dados e examinar equipamentos espalhados pela rede. O RFC cita máquinas como nós de comutação de pacotes, PADs e gateways. Para quem trabalha num programa isolado, parar a execução no ponto exato é uma técnica comum. Para um gateway ativo, a pausa pode virar o defeito mais visível do dia. O texto diz que esperar comandos remotos depois de parar no breakpoint é pouco útil quando o alvo precisa atender demandas em tempo real.

O protocolo não se limitava a mostrar um estado. Suas mais de quarenta ordens cobriam transferência de dados, administração de objetos, controle de execução e breakpoints. O host podia consultar e alterar memória, mover dados, iniciar execução e receber avisos de exceção. A mesma profundidade que prometia encontrar uma falha também dava ao instrumento condições de perturbar a máquina. O desenho precisava decidir quanto dessa capacidade caberia dentro de um alvo com poucos recursos.

O RFC respondeu com três níveis. LOADER_DUMPER era o conjunto pequeno para carregamento, extração, leitura, escrita e início de execução. BASIC_DEBUGGER acrescentava comandos de controle e breakpoints comuns. FULL_DEBUGGER incluía comandos de máquina de estados finitos nos breakpoints; watchpoints eram opcionais. Os níveis se acumulavam. Quando a conexão de transporte era aberta, o host enviava HELLO e recebia HELLO_REPLY com a versão, o nível, o tipo de sistema e o formato de endereço do alvo. A conversa começava por descobrir qual máquina estava ali, não por presumir que todo boot loader pudesse executar um laboratório completo.

Enviar a regra antes do problema

Um breakpoint comum para o objeto e comunica seu estado ao host. O técnico decide depois o próximo comando. O tempo da rede fica dentro da pausa. No nível completo, o host podia enviar antes uma lista de comandos condicionais. Quando o endereço observado era executado, o alvo escolhia uma lista segundo seu estado local. Contador e ponteiros serviam à lógica; o alvo podia examinar informações, atualizar o estado do breakpoint e relatar o evento sem pedir ao host uma nova decisão naquele segundo.

Essa arquitetura não tornava a depuração invisível. As listas ocupavam memória, a avaliação consumia ciclos, e algumas ações ainda podiam parar ou controlar objetos. O próprio RFC admitia limites possíveis para número de breakpoints, estados, condições e tamanho das listas. A escolha era colocar uma decisão sensível ao tempo onde o evento ocorria. O host preparava a instrução; o alvo a executava com os dados ainda à mão e com alguma chance de manter o serviço principal.

Cada breakpoint ou watchpoint pertencia a uma conexão de depuração e a um endereço. Isso se tornava relevante porque o projeto previa mais de um usuário investigando o mesmo alvo, se a implementação e o transporte aceitassem várias conexões. Havia sessões e objetos a separar dentro da máquina em exame. A urgência do diagnóstico não aumentava sua memória disponível nem reduzia automaticamente a latência dos clientes que dependiam dela.

O host caiu, a conexão ficou

A sessão começava com uma conexão de transporte, HELLO e HELLO_REPLY; terminava quando o host fechava a conexão. O alvo deveria então liberar os recursos reservados. Mas o host podia cair no meio. Num alvo com capacidade para uma só conexão, uma sessão meio morta poderia bloquear todo o serviço de depuração. Welles e Milliken sugeriram detectar a morte da conexão ou permitir que uma conexão nova tomasse o lugar de uma inativa. Para a segunda opção, era necessário manter um canal capaz de receber pedidos enquanto o primeiro ainda ocupava o recurso.

O texto recomendava o RDP do RFC 908 como transporte e admitia TCP com maior custo de recursos conforme sua análise. Essa camada tinha de entregar mensagens ordenadas e confiáveis, distinguindo conexões. Uma mensagem podia carregar várias ordens LDP, mas cada ordem precisava caber inteira nela. O transporte movia instruções. Não decidia quanto trabalho um breakpoint residente podia consumir, nem quando um host desaparecido perderia seu lugar.

O RFC 909 foi publicado como proposta experimental. Sua especificação documenta uma solução desenhada, sem medir adoção em gateways reais. A pergunta que deixou é mais útil que um catálogo de comandos: até que ponto uma organização pode aprofundar o acesso remoto a uma máquina viva antes de transformar a investigação no motivo de o serviço parar? Um breakpoint perfeito para o técnico pode ser um péssimo minuto para todos os demais.

Fontes