Guia de rastreamento dinâmico Solaris

Otimização de chamada de laço

Quando uma função termina ao chamar outra função, o compilador pode se envolver na otimização de chamada de laço, na qual a função que está sendo chamada reutiliza o quadro de pilha do chamador. Este procedimento é mais usado na arquitetura SPARC, onde o compilador reutiliza a janela do registro do chamador na função que está sendo chamada para minimizar a pressão da janela do registro.

A presença desta otimização faz com que o teste return da função que está chamando seja acionado antes do teste entry da função chamada. Esta ordem pode causar um pouco de confusão. Por exemplo, se você quisesse registrar todas as funções de uma função específica e quaisquer funções que esta função chama, teria que usar o script seguinte:

fbt::foo:entry
{
	self->traceme = 1;
}

fbt:::entry
/self->traceme/
{
	printf("called %s", probefunc);
}

fbt::foo:return
/self->traceme/
{
	self->traceme = 0;
}

Entretanto, se foo() terminar em uma chamada de laço otimizada, a função de chamada de laço, e portanto quaisquer funções que ela chama, não seriam capturadas. O kernel não pode ser dinamicamente desotimizado durante o uso, e o DTrace não quer entrar em uma situação falsa sobre como o código é reestruturado. Portanto, você deve estar ciente de quando a otimização de chamada de laço deve ser usada.

A otimização de chamada de laço deve ser usada em um código-fonte semelhante ao exemplo seguinte:

	return (bar());

Ou em um código-fonte semelhante ao exemplo seguinte:

	(void) bar();
	return;

Contrariamente, o código-fonte da função que termina como o exemplo seguinte não pode ter a sua chamada à bar() otimizada, pois a chamada à bar() não é uma chamada de laço:

	bar();
	return (rval);

Você pode determinar se uma chamada sofreu otimização de chamada de laço usando a técnica seguinte:

Devido à arquitetura do conjunto de instruções, a otimização de chamada de laço é muito mais comum em sistemas SPARC do que em sistemas x86. O exemplo seguinte usa mdb para descobrir a otimização de chamada de laço na função dup() do kernel:


# dtrace -q -n fbt::dup:return'{printf("%s+0x%x", probefunc, arg0);}'

Enquanto este comando está sendo executado, execute um programa que realize um dup(2), tal como um processo bash. O comando acima deve fornecer uma saída semelhante ao exemplo seguinte:


dup+0x10
^C

Agora examine a função com mdb:


# echo "dup::dis" | mdb -k
dup:                            sra       %o0, 0, %o0
dup+4:                          mov       %o7, %g1
dup+8:                          clr       %o2
dup+0xc:                        clr       %o1
dup+0x10:                       call      -0x1278       <fcntl>
dup+0x14:                       mov       %g1, %o7

A saída mostra que dup+0x10 é uma chamada para a função fcntl() e não uma instrução ret. Portanto, a chamada à fcntl() é um exemplo de otimização de chamada de laço.