PR jit/69144: Ensure that libgccjit's tempdir is fully cleaned-up

There were a couple of ways that libgccjit could fail to unlink all
of its tempfiles, leading to /tmp/libgccjit-* tempdirs lingering
after the build:
- dumpfiles requested by gcc_jit_context_enable_dump
- ahead-of-time compilation artifacts which lingered in the tempdir
  after they've been copied up to the output_path.  This was only
  the case for GCC_JIT_OUTPUT_KIND_OBJECT_FILE and
  GCC_JIT_OUTPUT_KIND_EXECUTABLE.

The following patch fixes these by introducing a vec of additional
cleanups to be performed by gcc:jit::tempdir's dtor.

In addition, if a gcc_jit_result * is leaked and
GCC_JIT_BOOL_OPTION_DEBUGINFO is enabled, the tempdir will also
not be cleaned up.  This was the case for tut04-toyvm/toyvm.cc
which the patch fixes by introducing a wrapper around
gcc_jit_result *.  Doing this required some updates to the
corresponding docs.

gcc/jit/ChangeLog:
	PR jit/69144
	* jit-playback.c (gcc::jit::playback::compile_to_file::postprocess):
	Potentially add the temporary artifact to the tempdir's list of
	tempfiles needing additional cleanup.
	(gcc::jit::playback::context::extract_any_requested_dumps): Likewise
	for the dumpfile.
	* jit-tempdir.c (gcc::jit::tempdir::~tempdir): Clean up additional
	tempfiles.
	* jit-tempdir.h (gcc::jit::tempdir::add_temp_file): New method.
	(gcc::jit::tempdir::m_tempfiles): New field.
	* docs/cp/intro/tutorial04.rst: Update for changes to toyvm.cc.
	* docs/examples/tut04-toyvm/toyvm.cc (class compilation_result):
	New.
	(toyvm_function::compile): Change return type from function ptr
	to a compilation_result.
	(toyvm_function::get_function_name): New accessor.
	(toyvm_function::m_funcname): New field.
	(get_function_name): Convert to...
	(toyvm_function::make_function_name): ...this new method.
	(toyvm_function::parse): Call make_function_name.
	(toyvm_function::compile): Convert return type from function ptr
	to a compilation_result.  Use get_function_name.
	(compilation_state::compile): Convert return type from
	gcc_jit_result * to a compilation_result.
	(test_script): Update for above changes, extracting the code from
	the compilation_result.
	(main): Likewise.
	* docs/_build/texinfo/libgccjit.texi: Regenerate.

From-SVN: r232582
This commit is contained in:
David Malcolm 2016-01-19 20:37:19 +00:00 committed by David Malcolm
parent f57fc96068
commit 199501ea41
7 changed files with 692 additions and 509 deletions

View File

@ -1,3 +1,34 @@
2016-01-19 David Malcolm <dmalcolm@redhat.com>
PR jit/69144
* jit-playback.c (gcc::jit::playback::compile_to_file::postprocess):
Potentially add the temporary artifact to the tempdir's list of
tempfiles needing additional cleanup.
(gcc::jit::playback::context::extract_any_requested_dumps): Likewise
for the dumpfile.
* jit-tempdir.c (gcc::jit::tempdir::~tempdir): Clean up additional
tempfiles.
* jit-tempdir.h (gcc::jit::tempdir::add_temp_file): New method.
(gcc::jit::tempdir::m_tempfiles): New field.
* docs/cp/intro/tutorial04.rst: Update for changes to toyvm.cc.
* docs/examples/tut04-toyvm/toyvm.cc (class compilation_result):
New.
(toyvm_function::compile): Change return type from function ptr
to a compilation_result.
(toyvm_function::get_function_name): New accessor.
(toyvm_function::m_funcname): New field.
(get_function_name): Convert to...
(toyvm_function::make_function_name): ...this new method.
(toyvm_function::parse): Call make_function_name.
(toyvm_function::compile): Convert return type from function ptr
to a compilation_result. Use get_function_name.
(compilation_state::compile): Convert return type from
gcc_jit_result * to a compilation_result.
(test_script): Update for above changes, extracting the code from
the compilation_result.
(main): Likewise.
* docs/_build/texinfo/libgccjit.texi: Regenerate.
2016-01-04 Jakub Jelinek <jakub@redhat.com>
Update copyright years.

File diff suppressed because it is too large Load Diff

View File

@ -297,15 +297,14 @@ Compiling the context
Having finished looping over the blocks and populating them with
statements, the context is complete.
We can now compile it, and extract machine code from the result:
We can now compile it, extract machine code from the result, and
run it:
.. literalinclude:: ../../examples/tut04-toyvm/toyvm.cc
:start-after: /* We've now finished populating the context. Compile it. */
:end-before: /* (this leaks "result" and "funcname") */
:start-after: /* Wrapper around a gcc_jit_result *. */
:end-before: /* Functions are compiled to this function ptr type. */
:language: c++
We can now run the result:
.. literalinclude:: ../../examples/tut04-toyvm/toyvm.cc
:start-after: /* JIT-compilation. */
:end-before: return 0;

View File

@ -28,6 +28,29 @@ along with GCC; see the file COPYING3. If not see
#include <libgccjit++.h>
/* Wrapper around a gcc_jit_result *. */
class compilation_result
{
public:
compilation_result (gcc_jit_result *result) :
m_result (result)
{
}
~compilation_result ()
{
gcc_jit_result_release (m_result);
}
void *get_code (const char *funcname)
{
return gcc_jit_result_get_code (m_result, funcname);
}
private:
gcc_jit_result *m_result;
};
/* Functions are compiled to this function ptr type. */
typedef int (*toyvm_compiled_func) (int);
@ -100,11 +123,19 @@ public:
int
interpret (int arg, FILE *trace);
toyvm_compiled_func
compilation_result
compile ();
const char *
get_function_name () const { return m_funcname; }
private:
void
make_function_name (const char *filename);
private:
const char *fn_filename;
char *m_funcname;
int fn_num_ops;
toyvm_op fn_ops[MAX_OPS];
friend struct compilation_state;
@ -149,8 +180,8 @@ toyvm_function::add_unary_op (enum opcode opcode,
add_op (opcode, operand, linenum);
}
static char *
get_function_name (const char *filename)
void
toyvm_function::make_function_name (const char *filename)
{
/* Skip any path separators. */
const char *pathsep = strrchr (filename, '/');
@ -158,14 +189,12 @@ get_function_name (const char *filename)
filename = pathsep + 1;
/* Copy filename to funcname. */
char *funcname = (char *)malloc (strlen (filename) + 1);
m_funcname = (char *)malloc (strlen (filename) + 1);
strcpy (funcname, filename);
strcpy (m_funcname, filename);
/* Convert "." to NIL terminator. */
*(strchr (funcname, '.')) = '\0';
return funcname;
*(strchr (m_funcname, '.')) = '\0';
}
toyvm_function *
@ -197,6 +226,7 @@ toyvm_function::parse (const char *filename, const char *name)
goto error;
}
fn->fn_filename = filename;
fn->make_function_name (filename);
/* Read the lines of the file. */
while ((linelen = getline (&line, &bufsize, f)) != -1)
@ -420,7 +450,7 @@ public:
void create_types ();
void create_locations ();
void create_function (const char *funcname);
gcc_jit_result *compile ();
compilation_result compile ();
private:
void
@ -462,24 +492,18 @@ private:
/* The main compilation hook. */
toyvm_compiled_func
compilation_result
toyvm_function::compile ()
{
compilation_state state (*this);
char *funcname;
funcname = get_function_name (fn_filename);
state.create_context ();
state.create_types ();
state.create_locations ();
state.create_function (funcname);
state.create_function (get_function_name ());
/* We've now finished populating the context. Compile it. */
gcc_jit_result *result = state.compile ();
return (toyvm_compiled_func)gcc_jit_result_get_code (result, funcname);
/* (this leaks "result" and "funcname") */
return state.compile ();
}
/* Stack manipulation. */
@ -767,7 +791,7 @@ compilation_state::create_function (const char *funcname)
} /* end of loop on PC locations. */
}
gcc_jit_result *
compilation_result
compilation_state::compile ()
{
return ctxt.compile ();
@ -825,7 +849,10 @@ test_script (const char *scripts_dir, const char *script_name, int input,
interpreted_result = fn->interpret (input, NULL);
CHECK_VALUE (interpreted_result, expected_result);
code = fn->compile ();
compilation_result compiler_result = fn->compile ();
const char *funcname = fn->get_function_name ();
code = (toyvm_compiled_func)compiler_result.get_code (funcname);
CHECK_NON_NULL (code);
compiled_result = code (input);
@ -894,7 +921,12 @@ main (int argc, char **argv)
fn->interpret (atoi (argv[2]), NULL));
/* JIT-compilation. */
toyvm_compiled_func code = fn->compile ();
compilation_result compiler_result = fn->compile ();
const char *funcname = fn->get_function_name ();
toyvm_compiled_func code
= (toyvm_compiled_func)compiler_result.get_code (funcname);
printf ("compiler result: %d\n",
code (atoi (argv[2])));

View File

@ -1888,6 +1888,7 @@ playback::compile_to_file::postprocess (const char *ctxt_progname)
case GCC_JIT_OUTPUT_KIND_ASSEMBLER:
copy_file (get_tempdir ()->get_path_s_file (),
m_output_path);
/* The .s file is automatically unlinked by tempdir::~tempdir. */
break;
case GCC_JIT_OUTPUT_KIND_OBJECT_FILE:
@ -1902,9 +1903,13 @@ playback::compile_to_file::postprocess (const char *ctxt_progname)
false, /* bool shared, */
false);/* bool run_linker */
if (!errors_occurred ())
copy_file (tmp_o_path,
m_output_path);
free (tmp_o_path);
{
copy_file (tmp_o_path,
m_output_path);
get_tempdir ()->add_temp_file (tmp_o_path);
}
else
free (tmp_o_path);
}
break;
@ -1918,6 +1923,7 @@ playback::compile_to_file::postprocess (const char *ctxt_progname)
if (!errors_occurred ())
copy_file (get_tempdir ()->get_path_so_file (),
m_output_path);
/* The .so file is automatically unlinked by tempdir::~tempdir. */
break;
case GCC_JIT_OUTPUT_KIND_EXECUTABLE:
@ -1932,9 +1938,13 @@ playback::compile_to_file::postprocess (const char *ctxt_progname)
false, /* bool shared, */
true);/* bool run_linker */
if (!errors_occurred ())
copy_file (tmp_exe_path,
m_output_path);
free (tmp_exe_path);
{
copy_file (tmp_exe_path,
m_output_path);
get_tempdir ()->add_temp_file (tmp_exe_path);
}
else
free (tmp_exe_path);
}
break;
@ -2279,7 +2289,7 @@ extract_any_requested_dumps (vec <recording::requested_dump> *requested_dumps)
filename = g->get_dumps ()->get_dump_file_name (dfi);
content = read_dump_file (filename);
*(d->m_out_ptr) = content;
free (filename);
m_tempdir->add_temp_file (filename);
}
}

View File

@ -121,7 +121,7 @@ gcc::jit::tempdir::~tempdir ()
fprintf (stderr, "intermediate files written to %s\n", m_path_tempdir);
else
{
/* Clean up .s/.so and tempdir. */
/* Clean up .s/.so. */
if (m_path_s_file)
{
log ("unlinking .s file: %s", m_path_s_file);
@ -132,6 +132,17 @@ gcc::jit::tempdir::~tempdir ()
log ("unlinking .so file: %s", m_path_so_file);
unlink (m_path_so_file);
}
/* Clean up any other tempfiles. */
int i;
char *tempfile;
FOR_EACH_VEC_ELT (m_tempfiles, i, tempfile)
{
log ("unlinking tempfile: %s", tempfile);
unlink (tempfile);
}
/* The tempdir should now be empty; remove it. */
if (m_path_tempdir)
{
log ("removing tempdir: %s", m_path_tempdir);
@ -145,4 +156,9 @@ gcc::jit::tempdir::~tempdir ()
free (m_path_c_file);
free (m_path_s_file);
free (m_path_so_file);
int i;
char *tempfile;
FOR_EACH_VEC_ELT (m_tempfiles, i, tempfile)
free (tempfile);
}

View File

@ -58,6 +58,10 @@ class tempdir : public log_user
const char * get_path_s_file () const { return m_path_s_file; }
const char * get_path_so_file () const { return m_path_so_file; }
/* Add PATH to the vec of tempfiles that must be unlinked.
Take ownership of the buffer PATH; it will be freed. */
void add_temp_file (char *path) { m_tempfiles.safe_push (path); }
private:
/* Was GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES set? If so, keep the
on-disk tempdir around after this wrapper object goes away. */
@ -74,6 +78,10 @@ class tempdir : public log_user
char *m_path_s_file;
char *m_path_so_file;
/* Other files within the tempdir to be cleaned up:
- certain ahead-of-time compilation artifacts (.o and .exe files)
- dumpfiles that were requested via gcc_jit_context_enable_dump. */
auto_vec <char *> m_tempfiles;
};
} // namespace gcc::jit