Quando o banco satura de CPU, a pergunta é só uma: quem está queimando processador agora? As views de sessão respondem com tempo acumulado desde o login, e isso quase não ajuda no calor do incidente. Aqui eu mostro como medir o CPU do momento, por sessão e por SQL, tirando duas fotos de DB CPU e olhando só a diferença. É o que faz o s_cpu.sql, da família g_gold.
O cenário: achar a CPU do momento
Standalone ou RAC, tanto faz. CPU do servidor perto de 100%, aplicação reclamando de lentidão, e você precisa achar a sessão culpada em segundos. Sem montar baseline de AWR, sem esperar o próximo snapshot, sem abrir o Enterprise Manager.
O incômodo de olhar CPU por sessão é que a estatística mostra tempo acumulado. Uma sessão logada há seis horas aparece com um número gigante mesmo parada agora. Outra que entrou faz dez segundos e está comendo um core inteiro aparece com um número pequeno. O acumulado mente sobre o presente.
A ideia: medir o delta, não o acumulado
A estatística DB CPU em V$SESS_TIME_MODEL guarda o tempo de CPU da sessão em microsegundos, desde que ela existe. Sozinho, esse valor é o acumulado da vida inteira da sessão. Mas se você lê o número, espera N segundos e lê de novo, a diferença entre as duas leituras é exatamente o CPU consumido naquele intervalo.
O s_cpu.sql faz isso dentro de um bloco PL/SQL. Tira o snapshot 1 do DB CPU por sessão, dorme N segundos, tira o snapshot 2 junto com os dados da sessão, e calcula a diferença. O resultado é a CPU do momento de cada sessão naquela janela, mais o percentual de quanto de um core ela ocupou.
As views por trás
Duas views sustentam a medição da CPU do momento.
GV$SESS_TIME_MODEL é a versão global da V$SESS_TIME_MODEL. No RAC ela traz a coluna INST_ID junto de SID, STAT_NAME e VALUE, e devolve as linhas de todas as instâncias ativas. O que importa é a linha onde STAT_NAME = 'DB CPU', com VALUE em microsegundos. Divide por 1.000.000 e vira segundos.
GV$SESSION dá o resto: usuário, SQL_ID, SQL_CHILD_NUMBER, STATE, EVENT, MACHINE e LAST_CALL_ET. Quando a sessão está ACTIVE, o LAST_CALL_ET é o tempo em segundos desde que ela entrou na chamada atual. Serve para saber se a sessão acabou de começar ou se está num call longo.
O join é por INST_ID e SID, filtrando só sessões ativas, com usuário preenchido e fora de espera ociosa (wait_class diferente de Idle).
O script
O s_cpu.sql pergunta o intervalo (de 1 a 60 segundos, Enter usa 10), tira os dois snapshots e imprime o resultado já ordenado por CPU. O coração dele é o bloco PL/SQL com os dois loops de coleta.
Carregando o script direto do GitHub…
Observação: o bloco acima é carregado ao online a partir do repositório no GitHub dbasobrinho, então reflete sempre a última versão publicada. Para baixar o script atualizado, acesse github.com/dbasobrinho/g_gold/blob/master/s_cpu.sql.
Quando você roda o script, a primeira coisa que ele faz é pedir o intervalo de medição em segundos. Esse intervalo é a janela entre as duas fotos do DB CPU: o script lê o valor, espera esse tempo e lê de novo. Você digita um número de 1 a 60, ou só tecla Enter para usar 10 segundos. Intervalo maior deixa a leitura mais estável (lembrando do buffer de 5 segundos do time model); intervalo menor fica mais instantâneo, porém com mais ruído.
O percentual de core sai de uma conta simples: delta_cpu / intervalo * 100. Se uma sessão queimou 9,8 segundos de CPU numa janela de 10 segundos, ela ocupou perto de 98% de um core inteiro naquele instante.
A saída fica assim, com o TOP de SQL por CPU do momento no topo e a lista de sessões logo abaixo, ordenada do maior consumidor para o menor:

Exemplo de saída do s_cpu.sql mostrando a CPU do momento num intervalo de 20 segundos, com instância, usuários e máquinas fictícios.
Os nomes acima são fictícios. No seu ambiente vão aparecer os usuários, machines e SQL_IDs reais das sessões ativas.
Lendo a saída campo a campo:
- TOP CPU (20s): a linha de cima é o ranking dos três SQL_IDs que mais consumiram CPU na janela medida, cada um com o total de segundos de CPU. É o resumo de quem mais pesou, antes de você descer para a tabela.
- SID/SERIAL: identifica a sessão, no formato sid,serial#,@inst_id (o @1 é o número da instância no RAC).
- USERNAME: o usuário Oracle dono da sessão.
- SQL_ID/CHILD: o SQL_ID em execução e, entre colchetes, o número do cursor filho (child number).
- %CPU: quanto de um core inteiro a sessão ocupou no intervalo. 100% é um core cheio; pode passar disso por arredondamento e pelo buffer do time model.
- CPU_s: os segundos de CPU que a sessão queimou na janela, o delta puro entre os dois snapshots.
- STATE/WAIT: ON CPU se a sessão estava rodando na CPU, ou o nome do wait event se estava esperando por algo.
- MACHINE: a máquina cliente de onde veio a conexão.
- ET: o LAST_CALL_ET, ou seja, há quantos segundos a sessão está na chamada atual.
Validação
Checagem rápida: rode o s_cpu.sql enquanto dispara um SQL pesado conhecido em outra sessão. O SQL_ID dele tem que aparecer no topo, com %CPU alto e STATE em ON CPU. Se aparecer, a medição está pegando o que deveria.
Troubleshooting
O SLEEP se adapta à versão
A partir da versão 1.3, o script escolhe o SLEEP sozinho. Ele tenta primeiro o DBMS_SESSION.SLEEP, que é público e existe no 18c em diante. Se o banco for mais antigo e esse procedure não existir, o tratamento de exceção cai para o DBMS_LOCK.SLEEP. Ou seja, roda nos dois mundos sem você precisar comentar ou descomentar linha nenhuma.
Uma ressalva para bases antigas (11g, 12c): se a conta não tiver GRANT EXECUTE ON DBMS_LOCK para o usuário, o fallback também falha, aí com ORA-06550 / PLS-00201. Isso não é defeito do script, é questão de privilégio: peça o grant ou rode com uma conta que já tenha execute em DBMS_LOCK.
Problema: intervalos muito curtos dão número impreciso
Sintoma: com intervalo de 1 ou 2 segundos, o resultado oscila demais entre execuções.
Causa: o time model bufferiza até 5 segundos de dados antes de publicar na view. Em janelas muito curtas, parte do CPU consumido ainda não apareceu no VALUE.
Solução: use intervalos de 5 a 10 segundos para leitura estável. Foi por isso que o default do script é 10.
O script é só leitura. Não altera parâmetro, não toca em dado, não mata sessão. O risco prático é próximo de zero, fora o bloqueio temporário da própria sessão.
Validação técnica
Confirmado na documentação oficial:
V$SESS_TIME_MODEL mostra tempo acumulado da sessão em microsegundos, com valores cumulativos e inteiros de 8 bytes (Oracle 19c Database Reference). A estatística DB CPU existe nessa view e na V$SYS_TIME_MODEL. As views GV$ trazem a coluna INST_ID e agregam as informações de todas as instâncias ativas do RAC (Oracle Database Reference). O DBMS_SESSION.SLEEP foi adicionado no 18c e o DBMS_LOCK.SLEEP ficou deprecated na mesma versão, continuando disponível por compatibilidade (Oracle 18c).
Conclusão
O s_cpu.sql resolve uma pergunta específica e comum: qual sessão está consumindo a CPU do momento. Dois snapshots de DB CPU, a diferença entre eles, e pronto, você tem o consumo do instante em vez do acumulado que não diz nada no meio de um incidente. O script está na família g_gold, no GitHub: github.com/dbasobrinho/g_gold. Depois de achar o SQL que mais pesa, o passo seguinte costuma ser tunar: às vezes um índice funcional já resolve.
Referências oficiais
- V$SESS_TIME_MODEL (Oracle Database Reference, 19c): a view de onde sai a estatística DB CPU, em microsegundos.
- DBMS_SESSION (Oracle Database PL/SQL Packages and Types Reference, 19c): o procedure SLEEP, público a partir do 18c.
- DBMS_LOCK (Oracle Database PL/SQL Packages and Types Reference, 19c): o procedure SLEEP usado no fallback, que exige grant de execute.