【问题标题】:Can I know the file and function names from where my function is called if it's from another .c?如果我的函数来自另一个.c,我可以知道调用我的函数的文件和函数名称吗?
【发布时间】:2020-10-31 20:33:57
【问题描述】:

我正在开发一个库,我想知道一些关于我提供的某个函数的调用者的数据。特别是,我需要知道调用我的函数(重新定义的 malloc)的文件名、函数名和行。

编辑:这是一个最小的工作示例,我可以检测用户何时调用 malloc 并将他“重定向”到我自己的 malloc 函数:

main.c:

#include <stdio.h>
#include <stdlib.h>
#include "myLib.h"

int main(){
    printf("Inside main, asking for memory\n");

    int *p = malloc(sizeof(int));
    *p = 3;

    free(p);

    return 0;
}

myLib.c:

#include "myLib.h"

void * myAlloc (size_t size){
    void * p = NULL;

    fprintf(stderr, "Inside my own malloc\n");
    
    p = (malloc)(size);

    return p;
    
}
#undef malloc
#define malloc(size) myAlloc(size)

myLib.h:

#ifndef MYLIB_H
#define MYLIB_H

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

#define malloc(size) myAlloc(size)

void * myAlloc(size_t size);
#endif

我尝试使用 _FILE_ _func_ 和 _LINE_ 关键字,但我无法让它工作,因为它在不同的模块中。

【问题讨论】:

  • 请提供一个可以编译的minimal reproducible example,但您在问题中提到的错误除外。
  • #define malloc(size) 产生语法错误:void *p = ;
  • 那么为什么这里的可变参数宏:#define malloc(size, ...) myMalloc (size, __FILE__) 应该是#define malloc(size) myMalloc (size, __FILE__)
  • 为什么需要知道?
  • @wobr 我用一个基本示例编辑了我的帖子,谢谢!

标签: c debugging c-preprocessor instrumentation


【解决方案1】:

你可以:

//mylib.h
#ifndef MYLIB_H
#define MYLIB_H

#include <stdlib.h>
// replace malloc in case it's already a macro
#ifdef malloc
#undef malloc
#endif

// I believe that from the standards point of view, this is undefined behavior
#define malloc(size) my_alloc(size, __FILE__, __LINE__, __func__)

#ifdef __GNUC__
// Allow compiler to do static checking.
__attribute__((__alloc_size__(1), __malloc__))
#endif
void *my_alloc(size_t size, const char *file, int line, const char *func);
//    ^^^^^^^^ I do not like camelCase case - one snake case to rule them all.

#endif

// mylib.c
#include "mylib.h" // do not ever mix uppercase and lowercase in filenames
#undef malloc      // undef malloc so we don't call ourselves recursively
#include <stdio.h>

void *my_alloc(size_t size, const char *file, int line, const char *func){
    fprintf(stderr, "Och my god, you wouldn't believe it!\n"
          "A function %s in file %s at line %d called malloc!\n",
         func, file, line);
    return malloc(size);
}

您可能还会看到assert does it。如果你的目标是 glibc,请阅读glibc docs replacing malloc

仍然如您所见,用户可能会执行(malloc)(size) cicumvent 宏扩展。你可以这样做:

void *my_alloc(size_t size, const char *file, int line, const char *func);

static inline void *MY_ALLOC(size_t size) {
    return my_alloc(size, NULL, 0, NULL);
}
#define MY_ALLOC(size)  my_alloc(size, __FILE__, __LINE__, __func__)

// if called with `malloc()` then MY_ALLOC is expanded
// if called as `(malloc)`, then just expands to MY_ALLOC.
#define malloc MY_ALLOC

int main() {
    malloc(10);    // calls my_alloc(10, "main.c", 62, "main");
    (malloc)(20);  // calls my_alloc(20, NULL, 0, NULL);
}

【讨论】:

  • 将 malloc 的大小(可能还有结果)添加到跟踪中可能是个好主意。
【解决方案2】:

GLIBC 为 malloc()、free()... 定义了隐藏符号,分别称为 __libc_malloc()、__libc_free()...

因此,您可以极大地简化调试宏。

m.h 中,只需定义以下内容:

#if DEBUG_LEVEL > 0

extern void *__libc_malloc (size_t bytes);

extern void *myMalloc(size_t size, const char *filename, const char *funcname, int line);

#define malloc(size) myMalloc(size, __FILE__, __FUNCTION__, __LINE__)

#endif

然后你可以编写一个定义myMalloc()的程序如下(例如文件名为m.c):

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "m.h"

#if DEBUG_LEVEL > 0

void *myMalloc(
              size_t      size,
              const char *filename,
              const char *funcname,
              int         line
             ) {

  fprintf(stderr, "malloc(%zu) called from %s/%s()#%d\n", size, filename, funcname, line);

  return __libc_malloc(size);
}

#endif

char *dup_str(char *string) {

  char *str = malloc(strlen(string) + 1);

  strcpy(str, string);

  return str;
}

int main(int ac, char *av[]) {

  char *str;

  if (av[1]) {
    str = dup_str(av[1]);
  } else {
    str = dup_str("NULL");
  }

  printf("String = '%s'\n", str);

  free(str);

  return 0;
}

当你在非调试模式下编译这个示例程序时:

$ gcc m.c -DDEBUG_LEVEL=0
$ ./a.out azerty
String = 'azerty'

在调试模式下编译程序时:

$ gcc m.c -DDEBUG_LEVEL=1
$ ./a.out azerty
malloc(7) called from m.c/dup_str()#27
String = 'azerty'

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-14
    • 2017-07-28
    • 1970-01-01
    • 2021-08-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多