家庭作业:我在某个地方有内存泄漏,但我找不到它。关于如何更有效地使用valgrind的任何提示?

时间:2013-10-31 04:32:08

标签: c memory-management memory-leaks valgrind

好的,首先,公平警告,这是一个班级的项目。除了修复内存泄漏之外,我不是在寻找任何帮助。我想我已经在这个C代码的空间上遵循了一些可怕的编码实践。无论如何,当我运行Valgrind来搜索内存泄漏发生的位置时,我一点都不清楚我错过了什么内存泄漏。我知道至少有两个字符串,我没有释放,但我malloc()编辑,纯粹基于valgrind输出的大小。因为我从项目中取出了一些无关的代码,valgrind行号很可能已经关闭,因此为了方便我将它们标记为注释。

一点背景,我正在编写shell的基本功能。它目前执行以下操作:

1.获取用户输入

2.将输入分解为cmdin结构

3.执行命令,前提是它没有管道。

4.从我创建的cmdin中取出空间,然后从步骤1开始。这就是问题发生的地方。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAX_SIZE 1024
#define MAX_CLEN 2097152
#define READ_END 0
#define WRITE_END 1
#define CHILD_STATUS 0
struct cmdin
{
    char *cmd;
    char **args;
    int nargs;
    int pipeflag;
};

//check if the last argument of the command passed to it is a pipe.
//if so, return the position of the pipe, otherwise, return 0
//if it has already found a pipe, it will return the position of that pipe
int conpipe(struct cmdin * cmd)
{
    if(!cmd->pipeflag)
    {
        if(!strcmp(cmd->args[cmd->nargs], "|"))
        {
            return cmd->nargs;
        }
        else
        {
            return 0;
            /* PROBLEM LINE BELOW */
        }  // line 46, where valgrind claims one of the problems exists
    }
    else
    {
        //printf("pipeflag: %d\n", cmd->pipeflag);
        return (cmd->pipeflag);
    }
}

//free the command after each runthrough
int freeze(struct cmdin cmd)
{
    int i;
    for(i=0; i <= (cmd.nargs); i++)
    {
        //printf("cmd.args[%d]: %s\n",i, cmd.args[i]);
        /* PROBLEM LINE BELOW*/
        free(cmd.args[i]); //this is line 62, as noted by valgrind
    }
    free(cmd.args);
    free(cmd.cmd);
    return 0;
}

//parse input, and add a null to the end
struct cmdin * parse(char *cmd)
{
    //allocate space for the command 
    struct cmdin *ped = malloc(sizeof(struct cmdin)); 
    //declare pipeflag, and nargs as 0
    ped->pipeflag = 0;
    ped->nargs = 0;
    //allocate space for the array of strings, and for the string cmd.
    ped->args =  malloc(sizeof(char*) * MAX_SIZE);
    ped->cmd = malloc(sizeof(char) * MAX_SIZE);
    //scan the input, and put the first argument into the cmd string
    sscanf(cmd, "%s %[^\n]", ped->cmd, cmd);
    //allocate space for the next string, and then copy cmd to it.
    ped->args[ped->nargs] =  malloc(sizeof(char) * MAX_SIZE);
    strcpy(ped->args[ped->nargs], ped->cmd);
    ped->pipeflag = conpipe(ped);

    /* PROBLEM LINE BELOW*/
    ped->nargs++;     // line 86, where valgrind claims the second leak is called?
    ped->args[ped->nargs] =  malloc(sizeof(char) * MAX_SIZE);
    //loop that allocates space for a command, and then assigns
    //the next arg to it.
    while(sscanf(cmd, " %s %[^\n]", ped->args[ped->nargs], cmd) == 2)
    {
        ped->pipeflag = conpipe(ped);
        ped->nargs++;
        ped->args[ped->nargs] = malloc(sizeof(char) * MAX_SIZE);
    }

    strncpy(ped->args[ped->nargs], cmd, strlen(cmd)-1);
    ped->nargs++;
    //ped->args[ped->nargs] = malloc(sizeof(char) * MAX_SIZE);
    ped->args[ped->nargs] = NULL;

    return ped;

}

int main()
{
    char cwd[MAX_CLEN];
    getcwd(cwd, sizeof(cwd));
    char input[MAX_CLEN]; 
    char *exit  = "exit\n";
    char *cd = "cd";

    //Command to take input
    printf("tosh$ ");
    fgets(input, sizeof input, stdin);    

    while(strcmp(input, exit))
    {
       //Command to parse input
       struct cmdin *cmd;
       cmd = parse(input);
       //if there is not a pipeflag
       if(!cmd->pipeflag)
       {
            //Change directories
            if(!strcmp(cd, cmd->args[0]))
            {
                chdir(cmd->args[1]); 
                getcwd(cwd, sizeof(cwd));
            }
            else if(strcmp(input, exit))
            {
                //command to run input
                int child_pid;
                child_pid = fork();
                if(child_pid == 0)
                {
                    if(strcmp(cmd->args[1],cmd->args[0]))
                    {
                        execvp(cmd->cmd, cmd->args);
                    }
                    else
                    {
                        free(cmd->args[1]);
                        cmd->args[1] = NULL;
                        cmd->nargs--;
                        execvp(cmd->cmd, cmd->args);

                    }
                }
                else
                {
                    wait(&child_pid);
                }
            }
            freeze(*cmd);
            free(cmd);
        }       
        //Command to take input
        printf("tosh$ ");
        fgets(input, sizeof input, stdin);
    }
    return 0;
}

注意:这个valgrind的输入看起来像这样:

tosh$ ls -al
tosh$ exit

valgrind Heap和Leak Summaries,如下:

HEAP SUMMARY:
==4901==     in use at exit: 4,096 bytes in 2 blocks
==4901==   total heap usage: 6 allocs, 4 frees, 24,600 bytes allocated
==4901== 
==4901== 2,048 bytes in 1 blocks are definitely lost in loss record 1 of 2 
==4901==    at 0x4C2B3F8: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4901==    by 0x400A48: parse (tosh.c:46)
==4901==    by 0x400C97: main (tosh.c:86)
==4901== 
==4901== 2,048 bytes in 1 blocks are definitely lost in loss record 2 of 2
==4901==    at 0x4C2B3F8: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4901==    by 0x400BDE: parse (tosh.c:62)
==4901==    by 0x400C97: main (tosh.c:86)
==4901== 
==4901== LEAK SUMMARY:
==4901==    definitely lost: 4,096 bytes in 2 blocks
==4901==    indirectly lost: 0 bytes in 0 blocks
==4901==      possibly lost: 0 bytes in 0 blocks
==4901==    still reachable: 0 bytes in 0 blocks
==4901==         suppressed: 0 bytes in 0 blocks

更新:根据要求。 我的Makefile:

CC=gcc
CFLAGS=-g -Wall
TARGET=tosh

$(TARGET): $(TARGET).c
    $(CC) $(CFLAGS) -o $(TARGET) $(TARGET).c

Valgrind版本:  的valgrind-3.7.0

gcc版本:       gcc(Ubuntu / Linaro 4.7.2-2ubuntu1)4.7.2

echo $ LD_PRELOAD:

(这没什么打印)

2 个答案:

答案 0 :(得分:4)

没有设法重现。但是有几个笔记。

  1. 使用您提供的错误和警告。

    gcc -Wall -Wextra -pedantic …
    
  2. 检查返回值和操作是否成功。这对于减少错误是必不可少的,特别是那些只有一英里的用户案例显示的错误。


  3. if (!(foo = malloc(size))) {
        perror("mem");
    }
    
    ...
    
    
    /* This line is no good: */
    sscanf(cmd, "%s %[^\n]", ped->cmd, cmd);
    /*      |                           |
     *      +----------+----------------+
     *                 |
     *                 +--- Not good. UB. (Input / Output same.)
     */
    

    可以添加一个例程来获取数量和大小......

    i = sscanf(cmd, "%s%n %[^\n]%n", ped->cmd, &n1, cmd, &n2);
    
    if (i < 2) {
        /* Debug print. */
        fprintf(stderr, 
                "%s:%-3d; "
                "sscanf => %d items and %d bytes str[%d]<%s>\n", 
                __FILE__, __LINENO_,
                i, n2, n1, ped->cmd
        );
    }
    

答案 1 :(得分:3)

好的,我只知道问题所在。大部分评论员都是对的。我在不同的源代码上运行valgrind。我一直在〜/ usr / bin中保留tosh的副本(在我的$ PATH中)。我忘了用我最近的更改(包括解决我的内存泄漏问题的那些更新)更新那段代码。

当我运行valgrind时,我使用了包含tosh.cosh:

的目录中的以下命令

$ valgrind tosh

我应该跑的是:

$ valgrind ./tosh

当我对命令进行更新时,代码运行良好,没有内存泄漏。谢谢你的帮助!对不起,我花了很长时间才弄清楚这一点。