прямой доступ к SGA / способ 1: C extension + ruby

 

 
План такой:

Пишем простой C extension модуль, который умел бы подсоединиться к куску shared memory, прочитать байт, два байта, четыре байта и строку указанной длины.

 
Впрочем, писать по шагам не будем, а просто приведём готовый код:

 

/* readSGA.c */
/* http://www.oracledba.ru */
#include <sys/types.h>
#include <sys/shm.h>
#include <string.h>
#include "ruby.h"

VALUE readSGA_attach(VALUE self, VALUE shmemid, VALUE shmemaddr) {
  int shmid;
  long shmaddr;
  shmid = NUM2INT(shmemid);
  shmaddr = NUM2LONG(shmemaddr);
  if (shmat(shmid,(void *)shmaddr,SHM_RDONLY) != (void *)-1) {
    return INT2NUM(0);
  } else {
    return INT2NUM(-1);
  }
}

VALUE readSGA_read1(VALUE self, VALUE addr) {
  VALUE ret;
  void *caddr = (void *)NUM2INT(addr);
  char v1;
  v1 = *(char *)caddr;
  ret = INT2NUM((int)v1);
  return ret;
}

VALUE readSGA_read2(VALUE self, VALUE addr) {
  VALUE ret;
  void *caddr = (void *)NUM2INT(addr);
  short v1;
  v1 = *(short *)caddr;
  ret = INT2NUM(v1);
  return ret;
}

VALUE readSGA_read4(VALUE self, VALUE addr) {
  VALUE ret;
  void *caddr = (void *)NUM2INT(addr);
  int v1;
  v1 = *(int *)caddr;
  ret = INT2NUM(v1);
  return ret;
}

VALUE readSGA_reads(VALUE self, VALUE addr, VALUE size) {
  VALUE ret;
  void *caddr = (void *)NUM2INT(addr);
  char *dest;
  int csize = NUM2INT(size);
  dest = (char *)malloc((size_t)csize);
  memcpy(dest,(void *)caddr,csize);
  ret = rb_str_new2(dest);
  free(dest);
  return ret;
}

static VALUE mreadSGA;

void Init_readSGA() {
    mreadSGA = rb_define_module("ReadSGA");
    rb_define_module_function(mreadSGA,"attach",readSGA_attach,2);
    rb_define_module_function(mreadSGA,"read1",readSGA_read1,1);
    rb_define_module_function(mreadSGA,"read2",readSGA_read2,1);
    rb_define_module_function(mreadSGA,"read4",readSGA_read4,1);
    rb_define_module_function(mreadSGA,"reads",readSGA_reads,2);
}

Далее, выполняем микро-скрипт

ruby -e 'require "mkmf"; create_makefile("readSGA")'

Для его успешного выполнения нужно, чтобы был установлен пакет типа 'ruby-dev'.

Далее, набираем 'make' после чего должен создаться файл readSGA.so.

Для тех, кто желает приобщиться к прекрасному без всех этих компиляций и установок пакетов мы выкладываем уже откомпилированную версию readSGA.so. Однако, т.к. эта версия собрана на Linux 2.4 с glibc2 и ruby 1.8.2, то и работать она будет только на чём-то таком похожем.

Теперь мы можем приступать к созданию простенького скрипта:

#!/usr/bin/env ruby
# readSGA.rb
# http://www.oracledba.ru
require 'readSGA'

shmid     = 131074
sgaBase   = 0x50000000
ksuseAddr = 0x5B2B1680
rowCount  = 170
rowSize   = 2408

def readstatus (statusid,ksuseflg)
  if (statusid & 11 == 1)
    status = 'ACTIVE'
  elsif (statusid & 11 == 0)
    if (ksuseflg & 4096 == 0)
      status = 'INACTIVE'
    else
      status = 'CACHED'
    end
  elsif (statusid & 11 == 2)
    status = 'SNIPED'
  elsif (statusid & 11 == 3)
    status = 'SNIPED'
  else
    status = 'KILLED'
  end
  return status
end

if ReadSGA.attach(shmid,sgaBase)==-1
  print "can't attach to SGA with id #{shmid}\n"
  exit
end

puts "'select from v$session' made by reading SGA directly:"
puts "       SID    SERIAL# USERNAME                       STATUS"
puts "---------- ---------- ------------------------------ --------"
memaddr = ksuseAddr
(1..rowCount).each do |i|
  ksspaflg = ReadSGA.read1(memaddr+1)
  ksuseflg = ReadSGA.read4(memaddr+1388)
  sid = i
  serial = ReadSGA.read2(memaddr+1382)
  username = ReadSGA.reads(memaddr+67,30)
  statusid = ReadSGA.read1(memaddr+1420)
  status = readstatus(statusid,ksuseflg)
  if (ksspaflg & 1 != 0) and (ksuseflg & 1 != 0)
    printf "%10d %10d %-30s %-8s\n",sid,serial,username,status
  end
  memaddr += rowSize
end

Если Вы подставите свои значения в начало скрипта, то у Вас получится работающий скрипт для "моментального" просмотра сессий без участия Oracle (но только до следующей перезагрузки Oracle'а :) )




назад далее