Undefined reference to `_imp__agread'

Again, what happens? Any error messages or other output?

test.dot is correct anyway as you can see here.

It’s just that no .png file is created in that directory which contains that graph image. This is what I am attempting to accomplish, so I’m wondering if my code does do that? Thanks!

I tried myself. It segfaults at g = agread(fp, 0) although fp is not NULL. Sorry. I have to call it a day now (I’m in CET). I’ll check in tomorrow to see how you’ve done.

Thanks for your help! I actually didn’t even get that error printed to my console because I am using Powershell, so I will try to work on it.

I don’t know what the error is offhand, but I would suggest introducing some error handling. E.g. fopen can fail. If you don’t check for failure, your program will blindly continue and likely not do what you expect.

Thanks! I did check whether fopen is failing using this, and determined that it is not:

if(fp == NULL){
        printf("Failed to open file/n");
    }
    else{
        printf("Opened file/n");
    }

Do you by any chance have any idea what agread would fail, given a valid file pointer?

agread can fail for numerous reasons. Though they really shouldn’t result in a segfault.

Looking at dot.demo/demo.c in the repository, I have the impression you have to call both gvContext and gvParseArgs before calling and of the ag* functions:

    /* set up a graphviz context */
    gvc = gvContext();

    /* parse command line args - minimally argv[0] sets layout engine */
    gvParseArgs(gvc, argc, argv);

    /* Create a simple digraph */
    g = agopen("g", Agdirected, 0);

I am mostly just guessing though. If you’re comfortable with it, you can use a debugger to figure out what’s going on. Options I’m aware of:

  1. GDB. Likely your best bet if you’re in some Unix-y environment under Windows. Startup GDB, attaching to your process, run until you hit the segfault, then bt to get a stack trace of where you are.
  2. LLDB. Does this work under Windows? Basically the same usage model as GDB.
  3. ASan. Does this work under Windows?
  4. Valgrind. Almost certainly does not work under Windows :frowning:
  5. rr. Almost certainly does not work under Windows :frowning:

These may sound scary, but they’re not so bad. And if you’re writing C code, you’ll need to resort to a debugger sooner or later.

Thanks! I was under the impression that gvParseArgs is used when you have user arguments you’d like to make note of. In my case, I don’t, so I believe I wouldn’t need to call that function? Not sure though…

Also, I’m using Powershell to run my program. Not sure if there are any good debuggers available for that?

The code snippet I am using I actually got from StackOverflow, here: Generate image of GraphViz graph given dot text c++ - Stack Overflow

It looks like:

bool saveImageGV(std::string file_path){
    GVC_t *gvc;
    Agraph_t *g;
    FILE *fp;
    gvc = gvContext();
    fp = fopen((file_path+".dot").c_str(), "r");
    g = agread(fp, 0);
    gvLayout(gvc, g, "dot");
    gvRender(gvc, g, "png", fopen((file_path+".png").c_str(), "w"));
    gvFreeLayout(gvc, g);
    agclose(g);
    return (gvFreeContext(gvc));
}

After doing something like this instead:

void export_graph_string(){
    GVC_t *gvc;
    Agraph_t *g;
    gvc = gvContext();
    char source[] = "digraph { a -> b; b -> c; c -> d; d -> a; }";

    g = agmemread(source);
    gvLayout(gvc, g, "dot");
    gvRender(gvc, g, "png", fopen("C:/Users/claud/Documents/UWaterloo/4A/Roseseed/test.png", "w"));
    gvFreeLayout(gvc, g);
    agclose(g);
    gvFreeContext(gvc);
}

It is now failing on the gvRender() line. A png file is created in the right location, but when I attempt to open it my image viewer says the file format is unsupported or the file is corrupted. The source string is just a simple graph example I found on the Graphviz website…

FWIW - this is what I use:

extern gvplugin_library_t gvplugin_core_LTX_library;
extern gvplugin_library_t gvplugin_dot_layout_LTX_library;
extern gvplugin_library_t gvplugin_neato_layout_LTX_library;

const char doLayout(const char *src, const char *format, const char *engine)
{
    lastErrorStr[0] = '\0';

    GVC_t *context = gvContext();
    gvAddLibrary(context, &gvplugin_core_LTX_library);
    gvAddLibrary(context, &gvplugin_dot_layout_LTX_library);
    gvAddLibrary(context, &gvplugin_neato_layout_LTX_library);

    agseterr(AGERR);
    agseterrf(vizErrorf);

    agreadline(1);

    Agraph_t *graph;
    char *result = NULL;
    unsigned int length;
    while ((graph = agmemread(src)))
    {
        if (result == NULL)
        {
            gvLayout(context, graph, engine);
            gvRenderData(context, graph, format, &result, &length);
            gvFreeLayout(context, graph);
        }

        agclose(graph);

        src = "";
    }

    return result;
}

hpcc-js-wasm/main.cpp at trunk · hpcc-systems/hpcc-js-wasm (github.com)

I only support textual outputs (like SVG) so you may need some extra libs…

Thanks! I do really need the output to be an image of some sort, thought. Like .png or .jpg.

In terms of this:

    gvAddLibrary(context, &gvplugin_core_LTX_library);
    gvAddLibrary(context, &gvplugin_dot_layout_LTX_library);
    gvAddLibrary(context, &gvplugin_neato_layout_LTX_library);

I’m not really sure what this does and when I integrate it, along with the extern headers, into my code, I get an ‘undefined reference to’ error.

No you shouldn’t have to do that. libcgraph where agread comes from is totally independent from libgvc where the gv* functions come from.

You shouldn’t need to use gvAddLibrary. It’s only when you’re on a platform which does not support explicit loading of plugins as dynamic-link libraries, DLLs, (shared objects, .so-files on Linux) that you have to do that. That shouldn’t be the case here, but maybe it is.

If you do use Gordon’s method with built-in plugin, you would need to also use something like cmd/dot/dot_builtins.c.

I would strongly advice against trying to run this from PowerShell, at least while debugging. It’s error handling is obscure, to say the least. I recommend that you run this from MINGW64 and make sure your PATH includes C:/Program Files/Graphviz/bin where the plugins reside together with the other DLLs.

Try to run the dot program to produce your png. If that succeeds, there shouldn’t be any problem with the libraries or plugins. Use dot -? for help.

Thanks for clearing that up. Unfortunately, whenever I try to run the program from MingW64, I get the following error:

$ ./sqlpsql.exe -i
C:/users/claud/Documents/UWaterloo/4A/Roseseed/SQLPtoSQL/sqlpsql.exe: error while loading shared libraries: ?: cannot open shared object file: No such file or directory

So I’ve just been using Powershell instead. How can I run it? Also, does it support user interaction? I could not run it in Git Bash either because it would just display all the output at the end after quitting.

Do you by any chance have any other ideas about how to solve this seg fault? I’m still very confused…

That suggests to me that your PATH is not set correctly in MINGW64.

Try:

dot test.dot -T png -o test.png
file test.png

which for me gives:

test.png: PNG image data, 481 x 224, 8-bit/color RGB, non-interlaced

and the PNG looks like this:
test

Yes, great! That works. I was able to get my code to do that using this:

char *export_graph = "dot test.dot -T png -o test.png";
    system(export_graph);

Thanks for all your help! I really appreciate it.

If you’re satisfied with that then I’m glad to have been of help.

For anyone who is interested in how the story continues, I get what I think is the same problem with gvRender as Claudia describes above, with basically the same program:

#include <cgraph.h>
#include <gvc.h>
#include <stdio.h>
#include <stdlib.h>

void export_graph_string(){

    char source[] = "digraph { a -> b; b -> c; c -> d; d -> a; }";
    Agraph_t *g = agmemread(source);

    GVC_t *gvc = gvContext();
    gvLayout(gvc, g, "dot");

    FILE *fp;
    char outfile[] = "test.png";
    if ((fp = fopen(outfile, "w+")) == NULL) {
        fprintf(stderr, "Error opening %s for writing\n", outfile);
        exit(1);
    }

    fprintf(stderr, "Calling gvRender\n");
    fflush(stderr);

    gvRender(gvc, g, "png", fp);

    fprintf(stderr, "Returned from gvRender\n");
    fflush(stderr);

    gvFreeLayout(gvc, g);
    agclose(g);
    gvFreeContext(gvc);
}

int main() {
  export_graph_string();
}

Building and running it with gdb (installed with pacman -S gdb) gives:

/c/Users/magja/Documents/graphviz-help-850$ make
gcc -Wall -o sqlpsql SQLP.c -I"C:/Program Files/Graphviz/include/graphviz" -L"C:/Program Files/Graphviz/lib" -lgvc -lcgraph
/c/Users/magja/Documents/graphviz-help-850$ gdb sqlpsql.exe
GNU gdb (GDB) 10.2
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-pc-msys".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from sqlpsql.exe...
(gdb) run
Starting program: /c/Users/magja/Documents/graphviz-help-850/sqlpsql.exe
[New Thread 14560.0x3068]
[New Thread 14560.0x3958]
[New Thread 14560.0x3bac]
Calling gvRender

Thread 1 received signal SIGSEGV, Segmentation fault.
0x00007ffbc1984ce1 in ntdll!RtlWaitOnAddress () from /c/WINDOWS/SYSTEM32/ntdll.dll
(gdb) bt
#0  0x00007ffbc1984ce1 in ntdll!RtlWaitOnAddress () from /c/WINDOWS/SYSTEM32/ntdll.dll
#1  0x00007ffbc199c4fc in ntdll!RtlEnterCriticalSection () from /c/WINDOWS/SYSTEM32/ntdll.dll
#2  0x00007ffbc199c362 in ntdll!RtlEnterCriticalSection () from /c/WINDOWS/SYSTEM32/ntdll.dll
#3  0x00007ffbbf6b4f82 in ucrtbase!fwrite () from /c/WINDOWS/System32/ucrtbase.dll
#4  0x00007ffbbf6b4f1c in ucrtbase!fwrite () from /c/WINDOWS/System32/ucrtbase.dll
#5  0x00007ffb994da2f6 in gvwrite () from /c/Program Files/Graphviz/bin/gvc.dll
#6  0x00007ffba8f82f02 in ?? () from /c/Program Files/Graphviz/bin/gvplugin_pango.dll
#7  0x00007ffb99020cb3 in cairo_surface_write_to_png () from /c/Program Files/Graphviz/bin/cairo.dll
#8  0x00007ffb99085c6d in cairo_append_path () from /c/Program Files/Graphviz/bin/cairo.dll
#9  0x00007ffb99078fc7 in cairo_append_path () from /c/Program Files/Graphviz/bin/cairo.dll
#10 0x00007ffb990791d7 in cairo_append_path () from /c/Program Files/Graphviz/bin/cairo.dll
#11 0x00007ffb99020a7d in cairo_pdf_surface_set_size () from /c/Program Files/Graphviz/bin/cairo.dll
#12 0x00007ffb99020d36 in cairo_surface_write_to_png_stream () from /c/Program Files/Graphviz/bin/cairo.dll
#13 0x00007ffba8f82440 in ?? () from /c/Program Files/Graphviz/bin/gvplugin_pango.dll
#14 0x00007ffb99499d19 in gvc!emit_once () from /c/Program Files/Graphviz/bin/gvc.dll
#15 0x00007ffb9949958b in gvc!emit_graph () from /c/Program Files/Graphviz/bin/gvc.dll
#16 0x00007ffb9949af59 in gvc!gvRenderJobs () from /c/Program Files/Graphviz/bin/gvc.dll
#17 0x00007ffb994d83f3 in gvc!gvRender () from /c/Program Files/Graphviz/bin/gvc.dll
#18 0x00007ff6d47f16b7 in export_graph_string ()
#19 0x00007ff6d47f1737 in main ()
(gdb)

This is way above my pay grade so I hope someone else can chime in. It would be nice to get to the bottom of this.

Looks like gvwrite is invoked with an invalid pointer or buffer length. Is this reproducible on Linux? If so, I can investigate further.

No, it isn’t. It render this:
test

This is way, way way above my knowledge of Windows, but I think years ago you had to be careful about opening files for writing in raw vs. text format, maybe something to do with newlines or other processing with text output. Sorry I can’t be more helpful and apologies if this is just misinformation now.

Hm yes, interesting point. The fopen mode can also include t (text mode) or b (binary mode). This has no effect on Linux, but seems to be relevant on Windows. From fopen, _wfopen | Microsoft Learn the default can be checked in _fmode, but regardless of what it is you’re going to have problems with the above code.

I believe the input file needs to be opened as "rt" and the output file as "wb". Not sure if this is the root cause, but it might be something to try.