前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用 Valgrind 检测 CGI 内存泄漏的简易方法

使用 Valgrind 检测 CGI 内存泄漏的简易方法

原创
作者头像
邵靖
修改2017-08-03 16:59:03
1.9K0
修改2017-08-03 16:59:03
举报
文章被收录于专栏:邵靖的专栏邵靖的专栏

项目中使用了基于CGIEx构建的CGI,并且通过CGI调用Protobuf API来完成一些动态解析proto定义之类的功能,上线前使用Valgrind的memcheck工具检测CGI是否存在内存泄漏的风险。

Valgrind的使用十分简单,通过设置一定的参数启动二进制可执行程序,并且在执行结束之后收集结果输出即可。但是我们的CGI是通过Apache运行的,不能直接使用Valgrind启动,Google一圈之后没有找到相关的实践,只好自己动手。

首先,直接执行CGI二进制可执行程序,可以看到进入了交互模式(Intractive Mode),并且提示等待用户输入(如下图)。不过如何输入参数?如何确定GET和POST的调用方法?以及如何区分两种方法的参数,却仍是未知数。

继续深入研究,全局搜索”cgihtml Interactive Mode”后找到了线索,在cgi-lib.c文件中找到了get_DEBUG函数定义如下:

代码语言:javascript
复制
char *get_DEBUG()
{
    int bufsize = 1024;
    char *buffer;
    int i = 0;
    char ch;

    if ((buffer = (char *)malloc(sizeof(char) * bufsize + 1)) == NULL)
        exit(1);

    fprintf(stderr,"\n--- cgihtml Interactive Mode ---\n");
    fprintf(stderr,"Enter CGI input string.  Remember to encode appropriate ");
    fprintf(stderr,"characters.\nPress ENTER when done:\n\n");
    while ( (i<=bufsize) && ((ch = getc(stdin)) != '\n') ) {
        buffer[i] = ch;
        i++;
        if (i>bufsize) {
            bufsize *= 2;
            if ((buffer = (char *)realloc(buffer,bufsize)) == NULL)
                exit(1);
        }
    }
    buffer[i] = '\0';
    fprintf(stderr,"\n Input string: %s\nString length: %d\n",buffer,i);
    fprintf(stderr,"--- end cgihtml Interactive Mode ---\n\n");
    return buffer;
}

继续搜索get_DEBUG的调用之处,发现只有一处调用,如下:

代码语言:javascript
复制
char* read_cgi_input(llist* entries)
{
    char *input;
    int status;

    /* check for form upload.  this needs to be first, because the
    standard way of checking for POST data is inadequate.  If you
    are uploading a 100 MB file, you are unlikely to have a buffer
    in memory large enough to store the raw data for parsing.
    Instead, parse_form_encoded parses stdin directly.

    In the future, I may modify parse_CGI_encoded so that it also
    parses POST directly from stdin.  I'm undecided on this issue,
    because doing so will make parse_CGI_encoded less general. */
    if ((CONTENT_TYPE != NULL) &&
        (strstr(CONTENT_TYPE,"multipart/form-data") != NULL ))
    {
        parse_form_encoded(entries);
        return NULL;
    }

    /* get the input */
    if (REQUEST_METHOD == NULL)
        input = get_DEBUG();
    else if (!strcmp(REQUEST_METHOD,"POST"))
    {
        input = get_POST();

        if ((input != NULL) && (strstr(input, "application/x-www-form-urlencoded") == NULL ))
        {
            return input;
        }
    }
    else if (!strcmp(REQUEST_METHOD,"GET"))
        input = get_GET();
    else { /* error: invalid request method */
        fprintf(stderr,"caught by cgihtml: REQUEST_METHOD invalid\n");
        exit(1);
    }
    /* parse the input */
    if (input == NULL)
    {
        return NULL;
    }

    status = parse_CGI_encoded(entries,input);
    FREE(input);
    return NULL;
}
  • 查看read_cgi_input函数,可以看到是通过宏REQUEST_METHOD来获取调用模式,宏REQUEST_METHOD的作用是获取环境变量“REQUEST_METHOD”。同时,还看到除了get_DEBUG函数之外,还定义了get_POST以及get_GET函数来分别处理POST和GET请求。
  • 其中get_DEBUG和get_GET函数的输出结果input都直接作为参数传递给parse_CGI_encoded函数进一步处理,所以DEBUG模式应该跟GET模式的输入参数是一致的。
  • 来看POST接口的调用方式,read_cgi_input函数首先通过CONTENT_TYPE环境变量确定输入参数的格式(text/json等),然后通过REQUEST_METHOD环境变量的取值(POST)进入调用get_POST函数的分支,并且在get_POST函数中通过CONTENT_LENGTH环境变量获取输出参数字符串的长度。

分析到这里,现在大致可以确定如何直接启动CGI二进制文件并输入参数了。

GET

CGI Interactive模式下,输入的参数就是通过GET方式调用时,URL后部所带的参数,形如: param1=val1&param2=val2,所以对于GET接口的测试的步骤很简单

  1. 使用valgrind启动CGI二进制文件进入Intractive模式valgrind --tool=memcheck --log-file=./valgrind_report.log --leak-check=full --show-reachable=yes --track-origins=yes ./cgi_get_sample
  2. 输入参数列表,并以回车结束。如果参数中有需要urlencode的字符请自行转换。starttime=2017-07-31%2014%3A59%3A31&endtime=2017-07-31%2015%3A59%3A31&id=1024
  3. 查看valgrind_report.log中的结果输出

POST

POST接口的调用方式稍微复杂一些。

  1. 设置环境变量export REQUEST_METHOD=POST export CONTENT_LENGTH=381 export CONTENT_TYPE="application/json"
  2. valgrind启动cgi,进入get_POST分支,等待用户输入,只是没有提示信息valgrind --tool=memcheck --log-file=./valgrind_report.log --leak-check=full --show-reachable=yes --track-origins=yes ./cgi_post_sample
  3. 输入json格式字符串参数{"id":1024,"name":"calvin"}
  4. 查看valgrind_report.log中的结果输出
  5. 测试完成后,将环境变量恢复 unset REQUEST_METHOD unset CONTENT_LENGTH unset CONTENT_TYPE

最后,贴一个集成POST/GET两种方式的脚本:

代码语言:javascript
复制
#!/bin/bash
# create by calvinshao

if [ $# -lt 3 ]; then
    echo "USAGE: $0 CGI_NAME DATA_LEN METHOD"
    exit 1
fi

if [ "$3" == "POST" ]; then
    #Set Env param
    export REQUEST_METHOD=POST
    export CONTENT_LENGTH=$2
    export CONTENT_TYPE="application/json"

    echo "Input JSON format string:"

    valgrind --tool=memcheck --log-file=./valgrind_report.log --leak-check=full --show-reachable=yes --track-origins=yes ./$1
else
    unset REQUEST_METHOD
    unset CONTENT_LENGTH
    unset CONTENT_TYPE

    valgrind --tool=memcheck --log-file=./valgrind_report.log --leak-check=full --show-reachable=yes --track-origins=yes ./$1
fi

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • GET
  • POST
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com