lunes, 17 de junio de 2013

Memoria de procesos Oracle sobre sistemas operativos Linux



Memoria de procesos Oracle



Mediante el siguiente programa escrito en C, se pretende determinar la cantidad de memoria consumida por los procesos Oracle, desde el punto de vista del sistema operativo.

Se debe recordar que la cantidad de memoria consumida por la instancia, debe de ser sumada a la cantidad de memoria configurada en la SGA y PGA.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <pwd.h>
#include <getopt.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <dirent.h>
#include <string.h>



#define LINE_BUFFER 100

/* Global variables */

const char *programName;
const char *username;
long memoryPageSize;
uid_t userid;
int totalprocess= 0;
int totalinstances= 0;

struct memory
{
  long total;
  long free;
  long used;
  long buffers;
  long cached;
  long rused;
  long rfree;
};

struct process
{
  long pid;
  long rss;
  long share;
  double memory;
  char instance[LINE_BUFFER];
  char cmdline[LINE_BUFFER];
  struct process *nextNode;
};

struct instance
{
  char instance[LINE_BUFFER];
  double memory;
  int processes;
  struct instance *nextNode;
};

typedef struct memory systeminfo;
typedef struct process *processinfo;
typedef struct instance *instanceinfo;

processinfo rootProcessNode= NULL;
processinfo tailProcessNode= NULL;

instanceinfo rootInstanceNode= NULL;

/* Insert root node to the instance linked list */

void insertRootInstance(processinfo node)
{
  rootInstanceNode= malloc(sizeof(struct instance));
  strcpy(rootInstanceNode->instance, node->instance);
  rootInstanceNode->memory= node->memory;
  rootInstanceNode->processes= 1;
  rootInstanceNode->nextNode= NULL;
  totalinstances++;
}

/* Add a new node to the instance linked list */

void addInstanceNode(processinfo node)
{
  instanceinfo instanceNode= rootInstanceNode;
  instanceinfo backNode= rootInstanceNode;
  while(instanceNode != NULL)
    {
       if((strcmp(instanceNode->instance, node->instance)) == 0)
{
 break;
}
       backNode= instanceNode;
       instanceNode= instanceNode->nextNode;
    }
  if(instanceNode == NULL)
    {
      instanceinfo nodeInfo= malloc(sizeof(struct instance));
      strcpy(nodeInfo->instance, node->instance);
      nodeInfo->memory= node->memory;
      nodeInfo->processes= 1;
      nodeInfo->nextNode= NULL;
      backNode->nextNode= nodeInfo;
      totalinstances++;
    }
  else
    {
      instanceNode->memory= instanceNode->memory + node->memory;
      instanceNode->processes= instanceNode->processes + 1;
    }
}

/* This function sorts the instance linked list */

void sortInstanceList()
{
  int i= 0;
  while(i < totalinstances)
    {
      instanceinfo instanceNode= rootInstanceNode;
      while(instanceNode->nextNode != NULL)
{
 if(instanceNode->memory < instanceNode->nextNode->memory)
   {
     struct instance backupInfo= *instanceNode;
     instanceNode->memory= instanceNode->nextNode->memory;
     instanceNode->processes= instanceNode->nextNode->processes;
     strcpy(instanceNode->instance, instanceNode->nextNode->instance);
     instanceNode->nextNode->memory= backupInfo.memory;
     instanceNode->nextNode->processes= backupInfo.processes;
     strcpy(instanceNode->nextNode->instance, backupInfo.instance);
   }
 instanceNode= instanceNode->nextNode;
}
      i++;
    }
}

/* Priint instance head message */

void printInstanceMessage()
{
  printf("%c[%d;%d;%dm",27,0,32,1);
  printf("\n**** Operating System Instance Memory Information ****\n\n");
  printf("%c[%d;%d;%dm",27,0,33,1);
  printf("Instance\t\tMemory (MB)\t\tProcesses\n");
  printf("%c[%dm", 27, 0);
}

/* Print instance linked list */

void printInstanceList()
{
  instanceinfo instanceNode= rootInstanceNode;
  printInstanceMessage();
  while(instanceNode != NULL)
    {
      printf("%.15s    \t\t\%.2lf\t\t\t%d\n", instanceNode->instance, instanceNode->memory, instanceNode->processes);
      instanceNode= instanceNode->nextNode;
    }
}

/* Function to add a new node to the instance linked list */

void insertInstanceNode(processinfo node)
{
  if(rootInstanceNode == NULL)
    {
      insertRootInstance(node);
    }
  else
    {
      addInstanceNode(node);
    }
}

/* Insert root node to the process linked list */

void insertRootNode(processinfo node)
{
  rootProcessNode= node;
  tailProcessNode= rootProcessNode;
  rootProcessNode->nextNode= NULL;
}

/* Add a node to the process linked list */

void addProcessNode(processinfo node)
{
  tailProcessNode->nextNode= node;
  tailProcessNode= node;
  tailProcessNode->nextNode= NULL;
}

/* Function to add a new node to the process linked list */

void insertProcessNode(processinfo node)
{
  if(rootProcessNode == NULL)
    {
      insertRootNode(node);
    }
  else
    {
      addProcessNode(node);
    }
  insertInstanceNode(node);
}


/* This function sorts process linked list */

void sortProcessList()
{
  int i= 0;
  while(i < totalprocess)
    {
      processinfo processNode= rootProcessNode;
      while(processNode->nextNode != NULL)
{
 if(processNode->memory < processNode->nextNode->memory)
   {
     struct process backupInfo= *processNode;
     processNode->pid= processNode->nextNode->pid;
     processNode->rss= processNode->nextNode->rss;
     processNode->share= processNode->nextNode->share;
     processNode->memory= processNode->nextNode->memory;
     strcpy(processNode->instance, processNode->nextNode->instance);
     strcpy(processNode->cmdline, processNode->nextNode->cmdline);
     processNode->nextNode->pid= backupInfo.pid;
     processNode->nextNode->rss= backupInfo.rss;
     processNode->nextNode->share= backupInfo.share;
     processNode->nextNode->memory= backupInfo.memory;
     strcpy(processNode->nextNode->instance, backupInfo.instance);
     strcpy(processNode->nextNode->cmdline, backupInfo.cmdline);
   }
 processNode= processNode->nextNode;
}
      i++;
    }
   
}

/* This function prints process linked list*/

void printProcessList()
{
  void printMemoryMessage();
  void printInfo(processinfo);
  processinfo processNode= rootProcessNode;
  printMemoryMessage();
  while(processNode != NULL)
    {
      printInfo(processNode);
      processNode= processNode->nextNode;
    }
}

/* Print program usage */

void printProgramUsage(FILE* stream, int exitCode)
{
  fprintf(stream, "%s valid options\n", programName);
  fprintf(stream, "-h --help Display program usage valid options.\n"
 "-u --username Opertaing system username to monitor.\n");
  exit(exitCode);
}


/* The goal of this function is to get the operating
   system memory page size */

long getMemoryPageSize(int limit)
{
  errno= 0;
  long pageSize;
  pageSize= sysconf(limit);
  if(pageSize != -1)
    {
      return pageSize;
    }
  else
    if (errno == 0)
    {
      fprintf(stderr, "Program error getting page size\n");
      exit(1);
    }
    else
      {
fprintf(stderr, "Invalid System Limit\n");
exit(errno);
      }
  return pageSize;
}

/* The goal of this function is to get the user id from
   operating system username that was got like argument on to
   program call */

uid_t getUserIdFromUsername(const char *username)
{
  struct passwd *userInfo;
  userInfo= getpwnam(username);
  if(userInfo == NULL)
    {
      fprintf(stderr, "Username not exist\n");
      exit(1);
    }
  return userInfo->pw_uid;
}

/* This function reads /proc/meminfo line by line to get
 memory parameters required */

systeminfo parseSystemMemoryInfo(FILE* fdMemory)
{
  systeminfo memoryUsage;
  char line[LINE_BUFFER];
  int countMatch= 0;
  long total= 0;
  long free= 0;
  long buffers= 0;
  long cached= 0;;
  while ((fgets(line, LINE_BUFFER, fdMemory)) != NULL && countMatch < 4)
    {
      if ((sscanf(line, "MemTotal:\t%ld", &total)) == 1)
        {
 countMatch++;
        }
        if ((sscanf(line, "MemFree:\t%ld", &free)) == 1)
        {
 countMatch++;
        }
        if ((sscanf(line, "Buffers:\t%ld", &buffers)) == 1)
        {
 countMatch++;
        }
        if ((sscanf(line, "Cached:\t%ld", &cached)) == 1)
        {
 countMatch++;
        }
    }
  memoryUsage.total= total / 1024;
  memoryUsage.free= free / 1024;
  memoryUsage.used= memoryUsage.total - memoryUsage.free;
  long plus_cached= (buffers + cached) / 1024;
  memoryUsage.rused= memoryUsage.used - plus_cached;
  memoryUsage.rfree= memoryUsage.free + plus_cached;
  return (memoryUsage);
}

/* Open operating system memory information file */

FILE *openMemInfo(const char *memPath)
{
  FILE *fdMemory;
  fdMemory= fopen(memPath, "r");
  if(fdMemory == NULL)
    {
      fprintf(stderr, "Error getting operating system memory information\n");
      exit(errno);
    }
  return (fdMemory);
}

/* This function open /proc/meminfo to get operating system
 * memory  usage*/

void printMemorySystemInfo()
{
  FILE *fdMemory;
  systeminfo memoryUsage;
  fdMemory= openMemInfo("/proc/meminfo");
  printf("%c[%d;%d;%dm",27,0,32,1);
  printf("**** Operating System Memory Information ****\n\n");
  printf("%c[%d;%d;%dm",27,0,33,1);
  printf("Total (MB)     \t Used (MB)    \t Free (MB)\n");
  printf("%c[%dm", 27, 0);
  memoryUsage= parseSystemMemoryInfo(fdMemory);
  printf("%ld", memoryUsage.total);
  printf("\t\t %ld (%ld%%)", memoryUsage.rused, (memoryUsage.rused * 100 / memoryUsage.total));
  printf("\t %ld (%ld%%)\n", memoryUsage.rfree, (memoryUsage.rfree * 100 / memoryUsage.total));
  fclose(fdMemory);
}

/* Check if the string argument is a number */

int isNumber(char *string)
{
    char *endPointer;
    long pid= strtol(string, &endPointer, 10);
    if (*endPointer == '\0')
    {
        return 1;
    }
    return 0;
}

/* Check if the process is real userd id or efective user id
   match whith oracle software owner */

int parseStatus(FILE *file)
{
    char line[LINE_BUFFER];
    char command[10];
    long realId= -1;
    long efectiveId= -1;
    while ((fgets(line, LINE_BUFFER, file)) != NULL)
    {
      if((sscanf(line, "Name:\t%s", command)) == 1)
{

 if((strcmp(command, "oracle")) != 0)
   {
     return 0;
   }
}
      if ((sscanf(line, "Uid:\t%ld\t%ld", &realId,&efectiveId)) == 1)
        {
  break;
        }
    }
    if(userid == realId || userid == efectiveId)
      {
return 1;
      }
    return 0;
}

/* Open a directory */

DIR *openDirectory(const char *dirPath)
{
    DIR *directory= NULL;
    directory= opendir(dirPath);
    return (directory);
}

/* Parse the line that was passed from the statm file. */

processinfo getMemoryInfo(char *line, processinfo processMemory)
{
  char *endPointer;
  strtol(line, &endPointer, 10);
  processMemory->rss= strtol(endPointer, &endPointer, 10);
  processMemory->share= strtol(endPointer, &endPointer, 10);
  return (processMemory);
}

/* Print process memory head message */

void printMemoryMessage()
{
  printf("%c[%d;%d;%dm",27,0,32,1);
  printf("\n**** Operating System Process Memory Information ****\n\n");
  printf("%c[%d;%d;%dm",27,0,33,1);
  printf("PID\t\tMemory (MB)\t\tCMD\t\t\t\tInstance\n");
  printf("%c[%dm", 27, 0);
}

/* Function to get command instance */

char *getInstance(char *cmdline)
{
  static char instance[10];
  static char word1[20];
  if((sscanf(cmdline,"oracle%s",word1)) == 1)
    {
      getInstance(word1);
    }
  else
    {
      char *token= strtok(cmdline, "_");
      while(token != NULL)
{
 strcpy(word1,token);
 token= strtok(NULL,"_");
}
    }
  strncpy(instance,word1,10);
  return (instance);
}

/* Function to print process memory information */

void printInfo(processinfo process)
{
  printf("%ld\t\t%.2lf\t\t\t%.15s\t\t\t%s\n",process->pid, process->memory, process->cmdline, process->instance);
}

/* This function open process directory, and read several files
   to get all process information */

processinfo getProcessInfo(char *directory, char *processDir, size_t pathSize, processinfo processMemory)
{
  processMemory= NULL;
  DIR *processDirectory= NULL;
  char *statusFile= malloc(pathSize);
  char *statmFile= malloc(pathSize);
  char *cmdlineFile= malloc(pathSize);
  snprintf(statusFile, pathSize, "/proc/%s/status", processDir);
  snprintf(statmFile, pathSize, "/proc/%s/statm", processDir);
  snprintf(cmdlineFile, pathSize, "/proc/%s/cmdline", processDir);
  processDirectory= openDirectory(directory);
  if(processDirectory != NULL)
    {
      FILE *fdstatus= fopen(statusFile, "r");
      FILE *fdstatm= fopen(statmFile, "r");
      FILE *fdcmdline= fopen(cmdlineFile, "r");
      if (fdstatus != NULL && fdstatm != NULL && fdcmdline != NULL)
{
 if(parseStatus(fdstatus))
   {
     processMemory= malloc(sizeof(struct process));
     char line[LINE_BUFFER];
     char line2[LINE_BUFFER];
     fgets(line, LINE_BUFFER, fdstatm);
     fgets(line2, LINE_BUFFER, fdcmdline);
     processMemory= getMemoryInfo(line, processMemory);
     processMemory->pid= atol(processDir);
     strcpy(processMemory->cmdline,line2);
              strcpy(processMemory->instance, getInstance(line2));
     processMemory->memory= (double) ((processMemory->rss - processMemory->share) * memoryPageSize) /  1024;
   }
 fclose(fdstatus);
 fclose(fdstatm);
 fclose(fdcmdline);
}
    }
  closedir(processDirectory);
  return (processMemory);
}


/* read all directories in the /proc virtual file system */

void printProcessInfo()
{
  processinfo process;
  DIR *procDirectory;
  struct dirent *directoryContent= NULL;
  procDirectory= openDirectory("/proc");
  if(procDirectory == NULL)
    {
      fprintf(stderr, "Error opening proc directory\n");
      exit(errno);
    }
  while ((directoryContent= readdir(procDirectory)) != NULL)
    {
      char *directoryName= directoryContent->d_name;
      if (isNumber(directoryName))
{
          char *directoryPath= malloc(256);
          snprintf(directoryPath, 256, "/proc/%s", directoryName);
 process= getProcessInfo(directoryPath,directoryName, 256, process);
 if(process != NULL)
   {
     insertProcessNode(process);
     totalprocess++;
   }
}
    }
  closedir(procDirectory);
}

void printByUsername()
{
  username= optarg;
  userid= getUserIdFromUsername(username);
  printMemorySystemInfo();
  printProcessInfo();
  sortInstanceList();
  printInstanceList();
  sortProcessList();
  printProcessList();
  exit(EXIT_SUCCESS);
}

int main(int argc, char *argv[])
{
  int option;
  const char* const short_options= "hu:";
  const struct option long_options[]=
  {
      {"help",0,NULL,'h'},
      {"username",1,NULL,'u'},
      {NULL,0,NULL,0}
  };
  programName= argv[0];
  memoryPageSize= getMemoryPageSize(_SC_PAGESIZE) / 1024;
  do
  {
      option = getopt_long(argc, argv, short_options, long_options, NULL);
      switch(option)
      {
      case 'h': printProgramUsage(stdout,0);
      case 'u':printByUsername();
      case '?': printProgramUsage(stderr,1);
      case -1:printProgramUsage(stderr,1);
break;
      default: abort();
      }
  }while(option != -1);
  exit(EXIT_SUCCESS);
}


El programa pude ser compilado mediante el siguiente comando:
gcc sourceC -o objectC
gcc memtrack.c -o memtrack

El programa puede ser usado de la siguiente manera:
objectC -u oracleSoftwareOwner
memtrack -u oracle


























Notas
  1. En caso de que alguno de los procesos evidencie un alto consumo de memoria, es recomendable validarlo con la cantidad de memoria consumida por la sesión en la base de datos.
  2. Debe recordar que un proceso con un alto consumo de memoria puede tener origen en la codificación de la unidades PL/SQL o de las sentencias de la aplicación.
  3. Este programa fue diseñado para dar solucion al problema 12.1 del libro Linux Programming Interface.