我自己的pgsql C函数中的内存泄漏

时间:2014-03-05 08:30:20

标签: c linux postgresql

我已经编写了自己的pgsql C-Function(在CentOS-5.2和pgsql-9.1上),它收集postgres后端进程正在使用的资源统计信息。

每个后端进程包含cpu_utime / cpu_stime / disk_read_size / disk_write_size。

我的代码如下......

#include "postgres.h"
#include "funcapi.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>

#define PGDATA_ENV_NM "PGDATA"
#define PROC_FULL_NAME_SIZE     1024
#define PROC_FULL_NAME  "/proc/"
#define MAX_PG_PROC_CNT 4096
#define TEMP_BUFFER_LEN 1024
#define DISK_READ_TITLE "read_bytes"
#define DISK_WRITE_TITLE "write_bytes"

#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif

typedef struct _backend_rsc{
        int si_proc_id;
        unsigned long long sui_cpu_utime;
        unsigned long long sui_cpu_stime;
        unsigned long long sui_disk_read;
        unsigned long long sui_disk_write;
} PROC_INFO;

PROC_INFO ST_PROC_NODES[MAX_PG_PROC_CNT];
int gi_tuple_idx = 0;

PG_FUNCTION_INFO_V1(v1c_get_backend_rsc);

Datum
v1c_get_backend_rsc(PG_FUNCTION_ARGS) {
        FuncCallContext     *funcctx;
        int                  call_cntr;
        int                  max_calls;
        TupleDesc            tupdesc;
        AttInMetadata       *attinmeta;

        if (SRF_IS_FIRSTCALL()) {
        MemoryContext   oldcontext;

        /* create a function context for cross-call persistence */
        funcctx = SRF_FIRSTCALL_INIT();

        /* switch to memory context appropriate for multiple function calls */
        oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);

        /* total number of tuples to be returned */
            funcctx->max_calls = fill_backend_node();

        if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
            ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                     errmsg("function returning record called in context "
                            "that cannot accept type record")));

        attinmeta = TupleDescGetAttInMetadata(tupdesc);
        funcctx->attinmeta = attinmeta;

        MemoryContextSwitchTo(oldcontext);
    }

    /* stuff done on every call of the function */
    funcctx = SRF_PERCALL_SETUP();
    call_cntr = funcctx->call_cntr;
    max_calls = funcctx->max_calls;
    attinmeta = funcctx->attinmeta;

    if (call_cntr < max_calls) {   /* do when there is more left to send */
        char       **values;
        HeapTuple    tuple;
        Datum        result;

        values = (char **) palloc(5 * sizeof(char *));
        values[0] = (char *) palloc(32 * sizeof(char));
        values[1] = (char *) palloc(32 * sizeof(char));
        values[2] = (char *) palloc(32 * sizeof(char));
        values[3] = (char *) palloc(32 * sizeof(char));
        values[4] = (char *) palloc(32 * sizeof(char));

        snprintf( values[0], 32, "%d",   ST_PROC_NODES[gi_tuple_idx].si_proc_id   );
        snprintf( values[1], 32, "%lu",  ST_PROC_NODES[gi_tuple_idx].sui_cpu_utime );
        snprintf( values[2], 32, "%lu",  ST_PROC_NODES[gi_tuple_idx].sui_cpu_stime );
        snprintf( values[3], 32, "%lu",  ST_PROC_NODES[gi_tuple_idx].sui_disk_read );
        snprintf( values[4], 32, "%lu",  ST_PROC_NODES[gi_tuple_idx].sui_disk_write);

        /* build a tuple */
        tuple = BuildTupleFromCStrings(attinmeta, values);

        /* make the tuple into a datum */
        result = HeapTupleGetDatum(tuple);

        /* clean up (this is not really necessary) */
        pfree(values[0]);
        pfree(values[1]);
        pfree(values[2]);
        pfree(values[3]);
        pfree(values[4]);
        pfree(values);
        gi_tuple_idx++;
        SRF_RETURN_NEXT(funcctx, result);
    }
    else {   /* do when there is no more left */
        gi_tuple_idx = 0;
        SRF_RETURN_DONE(funcctx);
    }
}

int fill_backend_node() {
        int     li_backend_count = 0;
        // read postgresql data_directory
        int     li_parent_pid = 0;
        char    ls_pgdata_dir[1024] = { 0x00 };
        strncpy(ls_pgdata_dir, getenv(PGDATA_ENV_NM), strlen(getenv(PGDATA_ENV_NM)));
        strcat(ls_pgdata_dir, "/postmaster.pid");

        // get postgresql group pid from postmaster.pid
        FILE *lfp_pg_pid_file = fopen(ls_pgdata_dir, "r");
        if( !lfp_pg_pid_file ) {
                return 1;
        }
        char temp[1024]={ 0x00 };
        fgets( temp, 1023, lfp_pg_pid_file ); 
        //fscanf(lfp_pg_pid_file, "%d", &li_parent_pid);
        sscanf( temp, "%d", &li_parent_pid );
        fclose(lfp_pg_pid_file);

        // read child process list from /proc/[1-9]*/status files which stores parent PID
        int     li_temp_pid = 0;
        int     li_temp_ppid = 0;
        unsigned long long lui_temp_cpu_utime=0, lui_temp_cpu_stime=0;
        DIR     *dir_info;
        struct  dirent  *dir_entry;
        FILE *lfp_stat_file = NULL;
        FILE *lfp_io_file = NULL;
        dir_info = opendir(PROC_FULL_NAME);
        char ls_stat_file_nm[PROC_FULL_NAME_SIZE];
        char ls_io_file_nm[PROC_FULL_NAME_SIZE];
        char ls_temp_string[TEMP_BUFFER_LEN] = { 0x00 };
        char *lcp_string_cursor = NULL;
        if(NULL != dir_info) {
                while(dir_entry = readdir(dir_info)) {
                        if (dir_entry->d_type == DT_DIR && *dir_entry->d_name > '0' && *dir_entry->d_name <= '9') {
                                memset(ls_stat_file_nm, 0x00, PROC_FULL_NAME_SIZE);
                                strcat(ls_stat_file_nm, PROC_FULL_NAME);
                                strcat(ls_stat_file_nm, dir_entry->d_name);
                                strcat(ls_stat_file_nm, "/stat");
                                lfp_stat_file = fopen(ls_stat_file_nm, "r");
                                if( !lfp_stat_file ) {
                                        return 1;
                                }
                                fscanf(lfp_stat_file, "%d %*s %*s %d %*s %*s %*s %*s %*s %*s %*s %*s %llu %llu", &li_temp_pid, &li_temp_ppid, &lui_temp_cpu_utime, &lui_temp_cpu_stime);
                                if( li_temp_ppid == li_parent_pid ) {
                                        ST_PROC_NODES[li_backend_count].si_proc_id    = li_temp_pid ;
                                        ST_PROC_NODES[li_backend_count].sui_cpu_utime = lui_temp_cpu_utime;
                                        ST_PROC_NODES[li_backend_count].sui_cpu_stime = lui_temp_cpu_stime;
                                        memset(ls_io_file_nm, 0x00, PROC_FULL_NAME_SIZE);
                                        strcat(ls_io_file_nm, PROC_FULL_NAME);
                                        strcat(ls_io_file_nm, dir_entry->d_name);
                                        strcat(ls_io_file_nm, "/io");
                                        lfp_io_file = fopen(ls_io_file_nm, "r");
                                        if( !lfp_io_file ) {
                                                return 1;
                                        }
                                        while( fgets(ls_temp_string, TEMP_BUFFER_LEN-1, lfp_io_file) ) {
                                                if( lcp_string_cursor = strstr(ls_temp_string, DISK_READ_TITLE) ) sscanf(ls_temp_string, "%*s %lu", &ST_PROC_NODES[li_backend_count].sui_disk_read);
                                                // not cancelled_write_bytes
                                                else if( *ls_temp_string != 'c' && (lcp_string_cursor = strstr(ls_temp_string, DISK_WRITE_TITLE)) ) sscanf(ls_temp_string, "%*s %lu", &ST_PROC_NODES[li_backend_count].sui_disk_write);
                                                else continue;
                                        }
                                        fclose(lfp_io_file);
                                        li_backend_count++;
                                }
                                fclose(lfp_stat_file);
                        }
                }
                closedir(dir_info);
        }
        //closedir(dir_info);
        return li_backend_count;
}

和contrib目录中的SQL文件位于......

之下
\echo Use "CREATE EXTENSION get_backend_rsc" to load this file. \quit

-- Register functions.
CREATE TYPE utype_get_backend_rsc AS
(
        proc_id         int,
        cpu_utime       numeric,
        cpu_stime       numeric,
        disk_read       numeric,
        disk_write      numeric
);

CREATE OR REPLACE FUNCTION get_backend_rsc()
    RETURNS SETOF utype_get_backend_rsc
    AS '$libdir/get_backend_rsc.so' , 'v1c_get_backend_rsc'
    LANGUAGE C IMMUTABLE STRICT;

-- Register a view on the function for ease of use.
CREATE VIEW get_backend_rsc AS
  SELECT * FROM get_backend_rsc();

GRANT SELECT ON get_backend_rsc TO PUBLIC;

问题是每当我执行该功能时,VmRSS/proc/$$/status增加4KB。我确信pallocpfree在那里,无处可能发生内存泄漏。

我错过了什么?任何建议都将深表感谢。

1 个答案:

答案 0 :(得分:0)

错过了{p> flose,但我仍然想知道为什么非关闭文件会导致内存泄漏。