Эта статья предназначена для технических специалистов, работающих с Oracle: администраторов баз данных, инженеров производительности, разработчиков инструментов мониторинга и исследователей внутренних механизмов СУБД. Это готовый пример низкоуровневого исследования, который можно адаптировать под собственные задачи. Мы покажем, как связать привычные SQL‑данные из v$sesstat с их реальным расположением в памяти процесса, и на конкретном примере проиллюстрируем методику, применимую и к другим внутренним структурам Oracle. Это первая статья из серии, она посвящена Oracle 11g, довольно древней версии Oracle, далее мы увидим как изменялся подход корпорации Oracle к хранению внутренних структур статистики.

Итак, начинаем: устанавливаем клиентскую сессию в Oracle. Фиксируем её ключевые идентификаторы: sid равен 69, ospid — 2945.

Теперь нужно определить адрес этой сессии в памяти. Для этого выполняем запрос к системному представлению v$session

SELECT saddr FROM v$session WHERE sid = 69;

В результате получаем адрес структуры сессии: 0000000188936D38. Именно по этому адресу в памяти процесса хранятся внутренние данные о сессии — и дальше мы будем использовать его, чтобы заглянуть «под капот» и увидеть статистику напрямую.

Сначала посмотрим на статистику моей сессии "традиционным" способом, с помощью запроса:
SELECT value FROM v$sesstat WHERE sid=69 ORDER BY statistic#;

0

1

1

333

3

0

0

37

5560

15

Давайте отформатируем результат в 2 колонки (зачем - будет понятно несколькими секундами позже):

0

1

1

333

3

0

0

37

5560

15

Далее "гвоздь программы"! Переходим от работы с SQL‑представлениями к низкоуровневой диагностике — и заглядываем прямо в память процесса Oracle.

  1. Мы подключаемся к процессу через GDB.

На сервере базы данных пользователем root, выполняем команду
gdb attach 2945

Команда gdb attach 2945 означает, что отладчик GDB присоединяется к операционной системе процессу с PID 2945 — это и есть OS‑процесс, который обслуживает сессию Oracle с sid = 69. Так у исследователя появляется доступ к адресному пространству этого процесса.

2. Чтение памяти по адресу со смещением.
В GDB выполняется команда:

(gdb) x/100g 0x0000000188936D38 + 0x140
  • Eё части означают:

    • x (examine) — команда GDB для просмотра содержимого памяти;

    • /100g — прочитать 100 элементов, где каждый элемент — 8 байт (giant, то есть 64‑битное значение);

    • 0x0000000188936D38 — адрес структуры сессии (saddr), полученный ранее из v$session;

    • +0x140 — смещение в байтах от этого адреса: автор предполагает (и затем подтверждает), что именно по этому смещению в SGA лежат статистики сессии.

Результат выполнения данной команды:

0x188936e78: 0 1
0x188936e88: 1 333
0x188936e98: 3 0
0x188936ea8: 0 37
0x188936eb8: 5560 15

Вы видите те же самые числа, что и в 2х колоночной табличке выше?

Это означает, что статистика сессии располагается в SGA неподалёку от адреса сессии — со смещением 0x140. Структура данных, судя по всему, представляет собой простой массив 8‑байтных значений, в котором статистики упорядочены по номеру “statistic#”.

Практическое применение.

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

Предположим, мы желаем понять, какая внутренняя функция Oracle ответственна за изменение статистики “session cursor cache hits”. Данная статистика имеет номер 498 (statistic#).
Как известно, в gdb есть возможность установить наблюдение за областью памяти:

awatch *(0x00000001889EDAC8+0x140+8*498)
  • awatch — тип точки наблюдения: срабатывает не только при записи, но и при чтении или записи в адрес (access watchpoint). Это полезно, если нужно поймать любой доступ к ячейке (например, обновление счётчика или его чтение для отдачи в представление).

  • * — разыменование: мы следим не за самим адресом, а за значением, которое лежит по этому адресу.

  • 0x00000001889EDAC8 — базовый адрес (адрес структуры сессии).

  • +0x140 — знакомое смещение: именно там, как выяснили ранее, начинается блок статистики сессии в SGA.

  • 8*498 — номер статистики 498 помноженный на размер отдельного значения (8 byte).

После 3-х кратного выполнения одного и того же запроса в моей SQL-сессии сработала поставленная ранее точка наблюдения (watchpoint) — процессор дошёл до определённого места в коде Oracle, и GDB остановил выполнение.
Останов произошёл в функции kkspsc0+372, т.е. в функции kkspsc0 по смещению 372

==> 0x00000000083f7e4a : mov QWORD PTR [rdi+r8*8+0x140],rsi

Что именно произошло

  • Сработал watchpoint. Тот самый awatch, который вы поставили на ячейку памяти для статистики № 498, "поймалось" обращение к этой ячейке (чтение или запись).

  • Указан адрес в коде: kkspsc0+372. kkspsc0 — это имя внутренней функции ядра Oracle (префикс kks обычно относится к компоненту Kernel SQL Compile/Execute). +372 — смещение внутри этой функции (в байтах от начала), то есть конкретная инструкция, которая обратилась к отслеживаемой ячейке.

  • Связь с вашими действиями. Три запуска SQL‑запроса привели к тому, что ядро Oracle в какой‑то момент обновило (или прочитало) нужную статистику — и именно в этот момент GDB прервал процесс.

Что это даёт для диагностики

  1. Прямая привязка действия к изменению метрики. Вы видите: «три раза выполнил запрос → в функции kkspsc0 на смещении +372 изменилась конкретная статистика». Это однозначное доказательство механизма учёта.

  2. Понимание, какая часть ядра отвечает за статистику. Теперь можно исследовать функцию kkspsc0: что она делает, в каких случаях вызывается, какие операции считаются.

  3. Возможность собрать стек вызовов. В GDB после остановки можно выполнить bt (backtrace) и увидеть полный стек: от SQL‑уровня до низкоуровневых функций. Это помогает понять цепочку событий, которая приводит к обновлению счётчика.

Ключевые выводы

  • Статистика сессии Oracle 11g физически располагается в SGA, со смещением 0x140 от базового адреса сессии (saddr), и организована как массив 8‑байтных значений, упорядоченных по STATISTIC#.

  • Стандартные представления вроде v$sesstat — это интерфейс над этой областью памяти: их данные можно не только читать через SQL, но и напрямую сопоставлять с содержимым памяти процесса.

  • С помощью GDB можно ставить watchpoint на отдельные счётчики и ловить моменты их изменения, получая точную привязку к коду ядра (например, к функции kkspsc0).

Комментарии (1)


  1. drema201 Автор
    05.08.2026 08:37

    Поправил PGA -> SGA (спасибо коллегам из RUOUG за замечание)
    Верифицировать можно через :

    select
        'SGA' LOC,
        KSMCHPTR,
        KSMCHIDX,
        KSMCHDUR,
        KSMCHCOM,
        KSMCHSIZ,
        KSMCHCLS,
        KSMCHTYP,
        KSMCHPAR
    from 
        x$ksmsp 
    where 
        to_number(substr('00000001889EDAC8', instr(lower('00000001889EDAC8'), 'x')+1) ,'XXXXXXXXXXXXXXXX') 
        between 
            to_number(ksmchptr,'XXXXXXXXXXXXXXXX')
        and to_number(ksmchptr,'XXXXXXXXXXXXXXXX') + ksmchsiz - 1
    

    кусочек из скрипта Tanel Poder.