domingo, 24 de agosto de 2014

Detección de memory leaks - Parte I

Hola, hoy voy a escribir como detectar memory leaks (fugas de memoria) en nuestro codigo C/C++ de nuestras librerias fm de BRM.

Read this post in english.

Muchos habrán oido sobre Valgrind para detectar memory leaks (entre otras funciones que posee), pero lamentablemente Valgrind aún no funciona en Solaris SPARC, por lo cual vamos a tener que usar otras herramientas cuando estemos trabajando en un Solaris SPARC. En la documentación de Oracle BRM se menciona Purify pero como hay que pagar licencia vamos a utilizar algo que ya poseea nuestro SO y no tengamos que pagar.

Para detectar memory leaks (referidos a *poid_t y *pin_flist_t) en el CM, lo primero que tenemos que hacer es modificar el pin.conf del CM agregando esta línea:
- - disable_pcm_mempool 1
Al agregar esa linea en el pin.conf del CM hemos deshabilitado el pool de memoria que manejan los CMs para procesar flist y poids, ahora la memoria se asigna desde el heap del sistema.

En la documentación online de Oracle podemos ver: To reduce thread contention on malloc calls, CMs include a memory pool mechanism for processing flists and POIDs. When flists and POIDs are allocated memory from a pool, problems with memory leaks are hidden. To detect memory leaks in your CM, before you run Purify or any other diagnostic utilities to test memory usage, disable the memory pool by adding the following entry in the CM pin.conf file so that memory is allocated from the system heap.

(recordar que cuando modificamos el pin.conf del CM, hay que reiniciar el CM para que tome los cambios realizados)

Hay muchas formas para detectar memory leaks. En este post vamos a utilizar Libumem y MDB. En este post explicaré 3 métodos (dejando el más práctico en último lugar, con un ejemplo paso a paso del mismo). La librería libumen solo está disponible en los sistemas Solaris 9 update 3 y superiores (para verificar que sistema estás utilizando: uname -a ).

Método 1

  1. Ejecutar:
    export LD_PRELOAD=libumem.so
  2. Ejecutar:
    export UMEM_DEBUG=audit
  3. De ser posible poner los puntos 1 y 2 en el .profile para no tener que estar ejecutándolos cada vez que se abre una nueva terminal.
  4. Compilar la librería fm_pol... con los flags -g y -lumen
  5. Iniciar el CM.
  6. Ejecutar el opcode que se quiere probar y ver el pid (process id) del CM que esta ejecutando el opcode.
  7. En otra terminal ejecutar:
    echo "::findleaks -d " | mdb -p pid_cm

Método 2

  1. Ejecutar:
    export LD_PRELOAD=libumem.so
  2. Ejecutar:
    export UMEM_DEBUG=audit
  3. De ser posible poner los puntos 1 y 2 en el .profile para no tener que estar ejecutándolos cada vez que se abre una nueva terminal.
  4. Compilar la librería fm_pol... con los flags -g y -lumen
  5. Iniciar el CM.
  6. Ejecutar el opcode que se quiere probar y ver el pid (process id) del CM que esta ejecutando el opcode.
  7. En otra terminal ejecutar:
    gcore pid_cm 
    El comando anterior generará un archivo llamado core.pid_cm
  8. Ejecutar:
     mdb core.pid_cm 
  9. Ejecutar:
    ::findleaks
  10. En caso de haber memory leaks la salida de ::findleaks nos mostrará cuantos buffers con memory leaks hay. 
  11. De cada buffer podemos obtener el stack trace ejecutando ::bufctl_audit para cada dirección bufctl. Notar que si se han leakeado (palabra inventada) mas de 16K la salida incluirá una lista de esos buffers leakeados incluyendo la cantidad de bytes y la dirección vmem_seg. Se pueden obtener los stack traces para esos buffers ejecutando ::vmem_seg -v para cada dirección vmem_seg.

Método 3

  1. Ejecutar:
    export LD_PRELOAD=libumem.so
  2. Ejecutar:
    export UMEM_DEBUG=audit
  3. De ser posible poner los puntos 1 y 2 en el .profile para no tener que estar ejecutándolos cada vez que se abre una nueva terminal.
  4. Compilar la librería fm_pol... con los flags -g y -lumen
  5. Iniciar el CM.
  6. Crear un archivo de texto con el flist de entrada del opcode que se quiere probar.
  7. Compilar testMemory.c con los flags -g y -lumen (al final del post esta el código fuente). 
  8. Ejecutar el programa testMemory con archivo conteniendo el flist de entrada y el opcode que se quiere probar. Por ejemplo: testMemory inFlist.txt PCM_OP_INV_POL_PREP_INVOICE
  9. El programa se conectará a BRM, ejecutará el opcode con el flist que se recibió y generará un archivo core para el CM que ejecutó el opcode, eso es todo lo que hace el programa (ejecuta el opcode y genera el core).
  10. Ejecutar:
     mdb core.pid_cm 
  11. Ejecutar:
    ::findleaks
  12. En caso de haber memory leaks la salida de ::findleaks nos mostrará cuantos buffers con memory leaks hay. 
  13. De cada buffer podemos obtener el stack trace ejecutando ::bufctl_audit para cada dirección bufctl. Notar que si se han leakeado (palabra inventada en este momento) mas de 16K la salida incluirá una lista de esos buffers leakeados incluyendo la cantidad de bytes y la dirección vmem_seg. Se pueden obtener los stack traces para esos buffers ejecutando ::vmem_seg -v para cada dirección vmem_seg.

Ejemplo paso a paso del método 3

  1. El ejemplo lo hare sobre la fm de invoicing $PIN_HOME/source/sys/fm_inv_pol : fm_inv_pol.so
  2. Los pasos 1, 2 y 3 ya los tengo hechos en mi ambiente (al ponerlos en el .profile) por lo que no los voy a realizar, pero si mostraré el valor de LD_PRELOAD y UMEM_DEBUG.
  3. pin>echo $LD_PRELOAD
    libumem.so
    pin>echo $UMEM_DEBUG
    audit
    
  4. Ahora hay que compilar con los flags especificados: -g y -lumen.
  5. pin>make all
    cc -c -g -xcg92 -lumen -lthread -ldl -lpinsys -shared -DFOR_CM -DFM_INV_POL_DLL -I/export/home/pin/7.5/include -I/export/home/pin/7.5/lib -DPCMCPP_CONST_SAFE -DFOR_CM  -DFM_INV_POL_DLL fm_inv_pol_config.c  fm_inv_pol_init.c fm_inv_pol_common.c fm_inv_pol_format_view.c fm_inv_pol_prep_invoice.c  fm_inv_pol_format_invoice.c  fm_inv_pol_format_invoice_doc1.c  fm_inv_pol_format_invoice_html.c  fm_inv_pol_format_invoice_xml.c  fm_inv_pol_format_invoice_xslt.c  fm_inv_pol_select.c  fm_inv_pol_utils.c  fm_inv_pol_post_make_invoice.c
    fm_inv_pol_config.c:
    fm_inv_pol_init.c:
    fm_inv_pol_common.c:
    fm_inv_pol_format_view.c:
    fm_inv_pol_prep_invoice.c:
    fm_inv_pol_format_invoice.c:
    fm_inv_pol_format_invoice_doc1.c:
    fm_inv_pol_format_invoice_html.c:
    fm_inv_pol_format_invoice_xml.c:
    fm_inv_pol_format_invoice_xslt.c:
    fm_inv_pol_select.c:
    fm_inv_pol_utils.c:
    fm_inv_pol_post_make_invoice.c:
    ld -o /export/home/pin/7.5/lib/fm_inv_pol.so -G -L/export/home/pin/7.5/lib fm_inv_pol_config.o  fm_inv_pol_init.o fm_inv_pol_common.o fm_inv_pol_format_view.o fm_inv_pol_prep_invoice.o  fm_inv_pol_format_invoice.o  fm_inv_pol_format_invoice_doc1.o  fm_inv_pol_format_invoice_html.o  fm_inv_pol_format_invoice_xml.o  fm_inv_pol_format_invoice_xslt.o  fm_inv_pol_select.o  fm_inv_pol_utils.o  fm_inv_pol_post_make_invoice.o -lm -lpsiu_for_cm
    
  6. Se inicial el CM.
  7. Como el fm que quiero probar es el de invoicng voy a hacer un flist para el opcode PCM_OP_INV_MAKE_INVOICE y lo voy a dejar en el archivo PCM_OP_INV_MAKE_INVOICE.txt. El formato de flist es el mismo que utilizaríamos para el testnap o de Developer Center.
  8. Compilo el archivo testMemory.c con los flags  -g y -lumen
  9. pin>make all
    cc -c -g -xcg92 -lumem -I/export/home/pin/7.5/include testMemory.c
    CC -o testMemory -g -xcg92 -lumem -I/export/home/pin/7.5/include -L/export/home/pin/7.5/lib -R:/export/home/pin/7.5/lib: -L/export/home/pin/7.5/lib testMemory.o -lportal -lmta -lpinsys -lsocket -lnsl -lgen -lposix4 -lpthread -ldl
    
  10. Ejecuto el programa testMemory con el archivo conteniendo el flist de entrada del opcode y el nombre del opcode: testMemory PCM_OP_INV_MAKE_INVOICE.txt
    PCM_OP_INV_MAKE_INVOICE
  11. pin>testMemory PCM_OP_INV_MAKE_INVOICE.flist PCM_OP_INV_MAKE_INVOICE
    
    
    Conectado a BRM
    
      PID  VSZ  RSS  F S   SZ    STIME        TIME %MEM COMMAND
    27895 62872 13896  0 S 7859 19:38:34       00:00  0.1 /export/home/pines/pin14/7.5/bin/cm
    27793 62872 36664  0 S 7859 19:38:16       00:01  0.1 /export/home/pines/pin14/7.5/bin/cm
    
    CM nuevo, pid = 27895
    
    
    Opcode PCM_OP_INV_MAKE_INVOICE ejecutado
    
    Output Flist:
    # number of field entries allocated 20, used 1
    0 PIN_FLD_POID           POID [0] 0.0.0.1 /invoice 2751918 0
    
    
      PID  VSZ  RSS  F S   SZ    STIME        TIME %MEM COMMAND
    27895 91544 43320  0 S 11443 19:38:34       00:02  0.2 /export/home/pines/pin14/7.5/bin/cm
    27793 62872 36664  0 S 7859 19:38:16       00:01  0.1 /export/home/pines/pin14/7.5/bin/cm
    
    
    Ejecutando: gcore 27895
    
    gcore: core.27895 dumped
    
    
  12. El programa se encargó de crear una conexión al CM, ver la memoria utilizada por el CM antes de ejecutar el opcode, ejecutar el opcode con el flist que le pasamos, ver la memoria utlizada por el CM despues de utlizar el opcode, generar un archivo core del CM y cerrar la conexión al CM.
  13. Hasta acá ya tenemos el archivo core para analizar con mdb y buscar memory leaks. En este ejemplo el archivo es: core.27895
  14. Ejecutamos mdb y luego ::findleaks para ver cuantos buffers con memory leaks hay.
  15. pin>mdb core.27895
    Loading modules: [ libumem.so.1 libc.so.1 libuutil.so.1 ld.so.1 ]
    > ::findleaks
    mdb: [fc000000, fc564000): couldn't read 16384 bytes at fc000000: no mapping for address
    CACHE     LEAKED   BUFCTL CALLER
    000a8008       1 0040a2e0 libcmpin.so`pcmmem_malloc_flistflds+0x34
    000a8008       1 0058da18 libcmpin.so`pcmmem_malloc_flistflds+0x34
    000a8008       1 0040a3b0 libcmpin.so`pcmmem_malloc_flistflds+0x34
    000a8008       1 005e6220 libcmpin.so`pcmmem_malloc_flistflds+0x34
    000a4008       1 0040ab68 libcmpin.so`pcmmem_malloc_flisthdr+0x34
    000a4008       1 004001a0 libcmpin.so`pcmmem_malloc_flisthdr+0x34
    000a4008       1 005814c8 libcmpin.so`pcmmem_malloc_flisthdr+0x34
    000a4008       1 005e6970 libcmpin.so`pcmmem_malloc_flisthdr+0x34
    0006c008       1 00635950 libcmpin.so`pcmmem_malloc_poid+0x34
    0006c008       1 0064dd40 libcmpin.so`pcmmem_malloc_poid+0x34
    ----------------------------------------------------------------------
       Total      10 buffers, 2880 bytes
    
  16. En este caso hay 10 buffers con memory leaks. Ahora hay que analizar cada uno de esos buffers. De cada buffer podemos obtener el stack trace ejecutando ::bufctl_audit para cada dirección bufctl.
  17. > 00635950::bufctl_audit
                ADDR          BUFADDR        TIMESTAMP           THREAD
                                CACHE          LASTLOG         CONTENTS
              635950           6319e0   9ebf699f553207                1
                                6c008                0                0
                     libumem.so.1`umem_cache_alloc+0x210
                     libumem.so.1`umem_alloc+0x60
                     libumem.so.1`malloc+0x28
                     libcmpin.so`pcmmem_malloc_poid+0x34
                     libcmpin.so`pcpxdr_poid+0xd0
                     libcmpin.so`pcpxdr_fld_list+0xe04
                     libcmpin.so`pcpxdr_op_decode+0x8c
                     libcmpin.so`pcp_receive_no_trans_cleanup+0x128
                     libcmpin.so`pcm_op_ex+0x9fc
                     libcmpin.so`pcm_op+0x1c
                     fm_inv_pol.so`fm_inv_pol_utils_memory_leak+0x3b0
                     fm_inv_pol.so`fm_inv_pol_utils_add_fields+0xf0
                     fm_inv_pol.so`fm_inv_pol_prep_invoice+0x124
                     fm_inv_pol.so`op_inv_pol_prep_invoice+0xf4
                     libcmpin.so`cm_pre_pcm_op+0x14f4
    
    >
    
    
  18. En el stack del 00635950 vemos que nuestra librería fm_inv_pol.so está dejando memoria leakeada (ya que inventamos una palabra vamos a usarla) en la función fm_inv_pol_utils_memory_leak (primer línea del stack que aparece con el nombre de nuestra librería) al invocar la función pcm_op (última línea antes de que aparezca el nombre de nuestra librería).
  19. Hasta aquí hemos detectado en que función y ejecutando que cosa es lo que está produciendo el memory leak. Ahora hay que ir al codigo donde se encuentra la función fm_inv_pol_utils_memory_leak, buscar todas las llamadas al PCM_OP (que es donde se está haciendo el malloc que luego no se libera) y ver que se está haciendo con el 5to parámetro de dicha función (el 5to parámetro es el único que reserva memoria en el PCM_OP). Si la variable correspondiente al 5to parámetro (un **pin_flist_t) no se está liberando (mediante un destroy), poniendo en otro flist (mediante un put) entonces ahí tenemos nuestro memory leak.
  20. Para este ejemplo elegí a propósito el PCM_OP porque he visto muchas veces que el pin_flist_t (el 5to parámetro de la función) donde se arroja el resultado de la ejecución del opcode no se le hace un destroy o un put o que en caso de error no se le hace un destroy y se están produciendo memory leaks.
    #include "pcm.h"
    void
    PCM_OP(
           pcm_context_t     *pcm_ctxp,
           int32             opcode,
           int32             flags,
           pin_flist_t       *in_flistp,
           pin_flist_t       **ret_flistpp,
           pin_errbuf_t      *ebufp);
    Si vemos la documentación online sobre el 5to parámetro:
    ret_flistpp
    A pointer to a pointer for passing back the return flist. See the individual opcode manual pages for the return flist specifications. All operations produce a return flist with at least the PIN_FLD_POID field on it. Other fields on the return flist depend on the operation being performed. The return flist is passed back even if an error occurred during the operation. It is the responsibility of the caller to destroy the return flist when it is no longer needed.
  21. Hasta acá hemos analizado un buffer de los 10 detectados. Analicemos otro, por ejemplo el 0040a3b0:
    > 0040a3b0::bufctl_audit
                ADDR          BUFADDR        TIMESTAMP           THREAD
                                CACHE          LASTLOG         CONTENTS
              40a3b0           4138c0   9ebf65e1425464                1
                                a8008                0                0
                     libumem.so.1`umem_cache_alloc+0x210
                     libumem.so.1`umem_alloc+0x60
                     libumem.so.1`malloc+0x28
                     libcmpin.so`pcmmem_malloc_flistflds+0x34
                     libcmpin.so`pini_flist_grow+0x48
                     libcmpin.so`pin_flist_create_with_size+0xc4
                     libcmpin.so`pin_flist_create+0x48
                     fm_bill_utils.so`fm_bill_utils_search_exchange_rate_from_db+0x420
                     fm_bill.so`fm_bill_init_config_currency_conversionrates+0x6c
                     fm_bill.so`fm_bill_init+0x1ec
                     libcm_main.so`mainThread+0x323c
                     main+8
                     _start+0x108
    
    >
    
    
  22. Aquí no se ve la llamada a nuestra librería fm_inv_pol.so, el memory leak esta en la librería fm_inv.so en la función fm_inv_make_invoice_event_search al ejecutar pin_flist_create. De esta librería no tenemos el código fuente, por lo tanto no podemos hacer nada para solucionar el memory leak que está provocando.
  23. Ya hemos analizado 2 buffers de 10. Los otros 8 quedan de tarea para el hogar. La mecánica es la misma para todos.
  24. En el próximo post (Detección de memory leaks - Parte II) voy a mostrar como a partir del resultado del ::bufctl_audit podemos obtener en qué línea del código fuente está produciéndose el memory leak.
testMemory.c
#include <stdio.h>
#include <stdio.h>
#include <string.h>
#include "pcm.h"
#include "pinlog.h"
#include <unistd.h>
#include <dlfcn.h>

#define MAX_PROCESS 256
#define STRING_LENGH 256

//ejecuta el comando recibido por parametro
void 
ejecutar_comando(
 char *comando);
 
//crea un flist que se encuentra contenido en un archivo
pin_flist_t* 
create_flist_from_file(
 char   *file_name,
 int64   db, 
 pin_errbuf_t *ebufp,
 int    print);
 
//Obtiene los CMs que se encuentran en ejecucion actualmente
void get_CMs_actuales(
 char array_CMs[MAX_PROCESS][STRING_LENGH]);

//Obtiene los CMs que se encuentran en ejecucion actualmente, pero que no estaban en ejecucion antes
void get_CMs_nuevos(
 char array_CMs_anteriores[MAX_PROCESS][STRING_LENGH],
 char array_CMs[MAX_PROCESS][STRING_LENGH]); 

//Genero el archivo core para cada uno de los CMs recibidos
void generar_cores(
 char array_CMs[MAX_PROCESS][STRING_LENGH]); 

 
 
int main(
 int    argc, 
 char   *argv[]) 
{
 pcm_context_t *ctxp;
 pin_flist_t  *out_flistp;
 pin_flist_t  *in_flistp;
 pin_opcode_t  opcode;
 pin_errbuf_t ebuf;
 int64   db = 1;
 char   *file_name = NULL;
 char   *opname = NULL;
 char   array_CMs_anteriores[MAX_PROCESS][STRING_LENGH];
 char   array_CMs_nuevos[MAX_PROCESS][STRING_LENGH];
 
 if(argc != 3){
  fprintf(stderr, "\nModo de uso: archivo_flist opcode_name\n\n");
  exit(1);
 }
 
 file_name = argv[1];
 opname = argv[2];
 opcode = pcm_opname_to_opcode(opname);
 
 if(opcode == 0){
  fprintf(stderr, "\nError, el opcode %s no existe\n\n", opname);
  exit(1);
 }

 PIN_ERR_CLEAR_ERR(&ebuf);
 
 //leo el flist que se encuentra en el archivo
 in_flistp = create_flist_from_file(file_name, db, &ebuf, 0);

 if (PIN_ERR_IS_ERR(&ebuf)) {
   fprintf(stderr, "Error al armar el flist del archivo %s\n", file_name);
   PIN_FLIST_DESTROY_EX(&in_flistp, NULL);
   exit(1);
 }
 
 if(in_flistp == NULL){
  fprintf(stderr, "Error al armar el flist del archivo %s\n", file_name);
  exit(1);
 }
 
 //Obtengo los CMs que se encuentran en ejecucion actualmente
 get_CMs_actuales(array_CMs_anteriores);
 
 //Me conecto a BRM, lo que generara un nuevo CM
 PCM_CONNECT(&ctxp, &db, &ebuf);
 
 if (PIN_ERR_IS_ERR(&ebuf)) {
   fprintf(stderr, "Error al ejecutar PCM_CONNECT\n");
   PIN_FLIST_DESTROY_EX(&in_flistp, NULL);
   exit(1);
 }
 
 fprintf(stdout, "\n\nConectado a BRM\n\n");
 
 //Muestro el estado de los CMs antes de ejecutar el opcode
 ejecutar_comando("ps -eo pid,vsz,rss,f,s,osz,stime,time,pmem,comm | head -1");
 ejecutar_comando("ps -eo pid,vsz,rss,f,s,osz,stime,time,pmem,comm | grep $PIN_HOME/bin/cm");
 
 //Obtengo los CMs que se iniciarion despues que la ultima vez que los revise, aqui estara el que ejecutara el opcode
 get_CMs_nuevos(array_CMs_anteriores, array_CMs_nuevos); 
 
 //Ejecuto el opcode con el flist del archivo
 PCM_OP(ctxp, opcode, 0, in_flistp, &out_flistp, &ebuf);
 PIN_FLIST_DESTROY_EX(&in_flistp, NULL);
 
 if (PIN_ERR_IS_ERR(&ebuf)) {
  fprintf(stderr, "\n\nError ejecutando el opcode %s\n", opname);
  PIN_ERR_LOG_EBUF(PIN_ERR_LEVEL_ERROR, "main error", &ebuf);
  PIN_ERR_CLEAR_ERR(&ebuf);
 } else {
  fprintf(stdout, "\n\nOpcode %s ejecutado \n", opname);
  fprintf(stdout, "\nOutput Flist:\n");
  PIN_FLIST_PRINT(out_flistp, NULL, &ebuf);
 }
 PIN_FLIST_DESTROY_EX(&out_flistp, NULL);
 
 fprintf(stdout, "\n\n");
 
 //Muestro el estado de los CMs despues de ejecutar el opcode
 ejecutar_comando("ps -eo pid,vsz,rss,f,s,osz,stime,time,pmem,comm | head -1");
 ejecutar_comando("ps -eo pid,vsz,rss,f,s,osz,stime,time,pmem,comm | grep $PIN_HOME/bin/cm");
 
 //genero los archivos core para todos los CMs nuevos, entre los cuales se encuentra el que ejecuto este opcode
 generar_cores(array_CMs_nuevos);
 
 //cierro el contex
 PCM_CONTEXT_CLOSE(ctxp, 0, &ebuf);
 if (PIN_ERR_IS_ERR(&ebuf)) {
   fprintf(stderr, "Error ejecutando PCM_CONTEXT_CLOSE\n");
   exit(1);
 }
 return 0;
}

//crea un flist que se encuentra contenido en un archivo
pin_flist_t* 
create_flist_from_file(
 char   *file_name,
 int64   db, 
 pin_errbuf_t *ebufp,
 int    print) 
{
 char   *buffer;
 char   linea[1048];
 pin_flist_t  *in_flistp = NULL;
 long    length = 0;
 FILE   *file = fopen(file_name, "r");

 if (file) {
  fseek(file, 0, SEEK_END);
  length = ftell(file) + 1;
  if(length > 15){
   fseek(file, 0, SEEK_SET);
   buffer = (char*)calloc(length, sizeof(char));
   if (buffer != NULL) {
    while (feof(file) == 0) {
     fgets(linea, sizeof(linea), file);
     if(strlen(linea) > 15  && feof(file) == 0 ){
      //solo me quedo con las lineas de mas de 15 caracteres
      strcat(buffer, linea);
     }
    } 
   } else {
    fprintf(stderr, "\nError, calloc de buffer devolvio NULL \n");
   }
  } else {
   //el archivo debe tener por lo menos los datos de un poid, si es demasiado chico ni eso tiene
   fprintf(stderr, "\nError, el contenido del archivo no es valido\n");
  }
  fclose(file);
 } else {
  fprintf(stderr, "\nError al abrir el archivo %s\n", file_name);
  return NULL;
 }
 
 if (buffer != NULL) {
  PIN_STR_TO_FLIST(buffer, db, &in_flistp, ebufp);
  if(print != 0){
   fprintf(stdout, "\nFlist leido del archivo %s\n", file_name);
   PIN_FLIST_PRINT(in_flistp, NULL, ebufp);
  }
  free(buffer);
 }
 return in_flistp;
}

//ejecuta el comando recibido por parametro
void 
ejecutar_comando(char *comando) 
{
 char buffer[256];
 char  *result = NULL;
 FILE *pipe = popen(comando, "r");
 
 if (!pipe) {
  fprintf(stderr, "\nError al ejecutar el comando: %s\n", comando);
  return;
 }
 result = (char*)malloc(256*sizeof(buffer));
 if(result == NULL){
  fprintf(stderr, "\nError, malloc de result devolvio NULL \n");
  return;
 }
 memset(result, '\0', sizeof(result));
 while(!feof(pipe)) {
  if(fgets(buffer, sizeof(buffer)-1, pipe) != NULL){
   strcat(result, buffer);
  }
 }
 pclose(pipe);
 fprintf(stdout, "%s", result);
 free(result);
}

//Obtiene los CMs que se encuentran en ejecucion actualmente
void get_CMs_actuales(
 char array_CMs[MAX_PROCESS][STRING_LENGH])
{
 char *comando = "ps -eo pid,vsz,rss,f,s,osz,stime,time,pmem,comm | grep $PIN_HOME/bin/cm";
 FILE *pipe = popen(comando, "r");
 int  i = 0;
 
    if (!pipe) {
  fprintf(stderr, "\nError al ejecutar el comando: %s\n", comando);
  return;
 }

 while(!feof(pipe)) {
  if(fgets(array_CMs[i], sizeof(array_CMs[i])-1, pipe) != NULL){
   i++;
  }
  if (i >= MAX_PROCESS) {
   fprintf(stdout, "\n\nSe supero la cantidad de procesos definidos, no se mostraran todos los procesos, solamente %d procesos\n\n", MAX_PROCESS);
   break;
  }
 }
 pclose(pipe);
}

//Obtiene los CMs que se encuentran en ejecucion actualmente, pero que no estaban en ejecucion antes
void get_CMs_nuevos(
 char array_CMs_anteriores[MAX_PROCESS][STRING_LENGH],
 char array_CMs[MAX_PROCESS][STRING_LENGH])
{
 char *comando = "ps -eo pid,vsz,rss,f,s,osz,stime,time,pmem,comm | grep $PIN_HOME/bin/cm";
 FILE *pipe = popen(comando, "r");
 char aux[STRING_LENGH];
 char *sub_str = NULL;
 char *str = NULL;
 char *str_ps = NULL;
 int  i = 0;
 
 if (!pipe) {
  fprintf(stderr, "\nError al ejecutar el comando: %s\n", comando);
  return;
 }

 fprintf(stdout, "\n");
 while(!feof(pipe)) {
  if(fgets(aux, sizeof(aux)-1, pipe) != NULL){
   str_ps = strtok_r(aux, " ", &sub_str);
   if(str_ps != NULL){
    int encontrado = 0;
    for(int j = 0; (j < MAX_PROCESS) && (array_CMs_anteriores[j] != NULL) && (strlen(array_CMs_anteriores[j]) > 0)  && (encontrado == 0); j++){
     str = strtok_r(array_CMs_anteriores[j], " ", &sub_str);
     if(strcmp(str_ps, str) == 0){
      //es un CM de los anteriories al context open
      encontrado = 1;
     }
    }
    if(encontrado == 0){
     fprintf(stdout, "CM nuevo, pid = %s\n", str_ps);
     strcpy(array_CMs[i], aux);
     i++;
    }
   }
  }
  if (i >= MAX_PROCESS) {
   fprintf(stdout, "\n\nSe supero la cantidad de procesos definidos, no se mostraran todos los procesos, solamente %d procesos\n\n", MAX_PROCESS);
   break;
  }
 }
 pclose(pipe);
}

//Genero el archivo core para cada uno de los CMs recibidos
void 
generar_cores(
 char  array_CMs[MAX_PROCESS][STRING_LENGH])
{
 char *sub_str = NULL;
 char *cm_pid = NULL;
 char comando[20];
 
 for(int i = 0; (i < MAX_PROCESS) && (array_CMs[i] != NULL) && (strlen(array_CMs[i]) > 0); i++){
  cm_pid = strtok_r(array_CMs[i], " ", &sub_str);
  //generando el archivo core para el cm_pid
  sprintf(comando, "gcore %s", cm_pid);
  fprintf(stdout, "\n\nEjecutando: %s\n\n", comando);
  ejecutar_comando(comando);
 }

}

Martín Falconi

No hay comentarios.:

Publicar un comentario