Chasser les pertes de mémoire en C sous Linux

Pour un fois je vais pouvoir parler de ce que je fais pour le boulot. En effet, j’ai planché sur un sujet qui peut servir à autrui.
Un des programmes écrit par un de mes prédécesseur avait une fâcheuse tendance à prendre de plus en plus de mémoire jusqu’à bloquer la machine (tssss un process lancé en root).

Chasser les fuites mémoire est toujours un peu hasardeux, il suffit de peu pour que ça devienne mission impossible.

J’ai travaillé principalement avec deux « outils » : valgrind et /proc

L’approche valgrind

valgrind est un utilitaire dédié à l’inspection de la mémoire d’un programme. Il note toutes les zones allouées et vérifie si elles sont correctement désallouées a la fin de celui-ci.

Il existe de très bons tuto sur le net pour son utilisation, par exemple celui-ci : http://fr.openclassrooms.com/informatique/cours/debuguer-facilement-avec-valgrind

L’approche /proc/*/status

Bon ce n’est pas vraiment une méthode très académique, mais elle permet de se rendre compte précisément de la consommation mémoire en temps réel de son programme.

Le noyau Linux met a disposition de l’utilisateur un grand nombre d’information sur les processus en cours d’exécution.

Beaucoup de ses informations sont disponibles dans le point de montage /proc.

En ce qui nous concerne, chaque processus, connu par son pid met a disposition les informations sur sa conso mémoire dans le fichier /proc/<pid du processus>/status.

 $ cat /proc/523/status
 Name: firefox
 State: S (sleeping)
 Tgid: 523
 Pid: 523
 PPid: 1
 TracerPid: 0
 Uid: 1000 1000 1000 1000
 Gid: 1000 1000 1000 1000
 FDSize: 128
 Groups: 4 20 24 25 29 30 33 44 46 103 104 114 115 126 146 147 148 1000 1004 1010
 VmPeak: 1259364 kB
 VmSize: 1164072 kB
 VmLck: 0 kB
 VmPin: 0 kB
 VmHWM: 412984 kB
 VmRSS: 365880 kB
 VmData: 622216 kB
 VmStk: 200 kB
 VmExe: 92 kB
 VmLib: 116232 kB
 VmPTE: 1784 kB
 VmSwap: 0 kB
 Threads: 34
 SigQ: 0/95953
 SigPnd: 0000000000000000
 ShdPnd: 0000000000000000
 SigBlk: 0000000000000000
 SigIgn: 0000000000001000
 SigCgt: 0000000f800144af
 CapInh: 0000000000000000
 CapPrm: 0000000000000000
 CapEff: 0000000000000000
 CapBnd: ffffffffffffffff
 Cpus_allowed: f
 Cpus_allowed_list: 0-3
 Mems_allowed: 00000000,00000001
 Mems_allowed_list: 0
 voluntary_ctxt_switches: 3852953
 nonvoluntary_ctxt_switches: 874330
 

La zone qui m’intéresse dans le cas d’une zone mémoire c’est la ligne VmRSS (qui vaut ici 365880 kB).
Celle-ci reflete la quantité de mémoire alloué actuellement par le processus. Attention à la valeur des autres zones, certaines comme VmSize comptabilisent aussi les librairies partagées et les fichiers ouverts (même s’il ne sont pas chargés en mémoire !), il ne s’agit donc pas forcement de mémoire utilisée !

Grace à la lecture de ce pseudo fichier il est possible de vérifier en cours d’utilisation la mémoire consommée et de tracer son évolution à partir du programme.

En somme Valgrind permet de chasser la mémoire utilisée et /proc de vérifier qu’il n’y a pas de cas tordus qui pourraient passer à travers.

Pour automatiser la chose

un petit script

pid=`ps -fe | grep [nom du process] |grep -v grep | awk '{print $2}'` && cat /proc/$pid//status |grep Vm

une petite fonction

Vu qu’on me l’a demandé j’ai aussi une petite fonction (très fortement inspirée de celle-ci ) pour afficher dans la log l’état de la mémoire (en même temps que les éléments clef du programme)

int memStats()
{
  char * line;
  char *vmsize;
  char *vmdata;
  char *vmrss;
  char *vmstk;

  size_t len;

  FILE *f;
 
  vmsize = NULL;
  vmdata = NULL;
  vmrss = NULL;
  vmstk = NULL;
  len = 128;
  line = malloc ( len * sizeof( char ) ) ;

  char * path2status = malloc( 128 * sizeof( char ) );
  sprintf (path2status, "/proc/%d/status", getpid() );

  f = fopen(path2status, "r");
  if (!f)
  {
    free ( path2status );
    free ( line );
    return 1;
  }
  free ( path2status );
  /* Read memory size data from /proc/pid/status */
  while (!vmsize || !vmrss || !vmdata || !vmstk )
  {
    if ( fgets ( line, len, f ) == NULL )
    {
      /* Some of the information isn't there, die */
      free(line);
      fclose( f ) ;
      return 1;
    }

    /* Find VmSize */
    else if (!strncmp(line, "VmSize:", 7))
    {
      vmsize = strdup(&line[7]);
    }

    /* Find VmRSS */
    else if (!strncmp(line, "VmRSS:", 6))
    {
      vmrss = strdup(&line[7]);
    }

    /* Find VmData */
    else if (!strncmp(line, "VmData:", 7))
    {
      vmdata = strdup(&line[7]);
    }
    /* Find VmStk */
    else if (!strncmp(line, "VmStk:", 6))
    {
      vmstk = strdup(&line[7]);
    }

  }
  free(line);

  fclose(f);

  /* Get rid of " kB\n"*/
  len = strlen(vmsize);
  vmsize[len - 4] = 0;
  len = strlen(vmstk);
  vmstk[len - 4] = 0;
  len = strlen(vmrss);
  vmrss[len - 4] = 0;
  len = strlen(vmdata);
  vmdata[len - 4] = 0;

  /* Output results to stderr */
  fprintf( stderr, "<%s;%s;%s;%s>\n", vmrss, vmsize, vmstk, vmdata);

  free(vmstk);
  free(vmsize);
  free(vmrss);
  free(vmdata);
  return 0;
}

Bonne chasse …

Publicités

Publié le 14 octobre 2013, dans Linux, Tips en vrac, et tagué , , . Bookmarquez ce permalien. Poster un commentaire.

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

%d blogueurs aiment cette page :