[m-rev.] for review: handle I/O errors when reporting statistics

Julien Fischer jfischer at opturion.com
Mon Apr 5 03:38:39 AEST 2021


For review by anynone.

Handle I/O errors when reporting statistics.

Detect I/O errors that occur when reporting statistics and handle them them by
throwing io.error exceptions.

runtime/mercury_report_stats.[ch]:
     Make MR_report_{standard,full_memory}_stats return zero on success
     or errno if an I/O error occurs.

library/io.m:
     Return a system_error indication from the report statistics predicates
     and throw an io.error exception on an error.

library/benchmarking.m:
     Implement report_stats/0 and report_full_memory_stats/0 by calling the
     report statistics predicates from the io module rather then by using
     foreign_procs. This ensures we get identical error handling behaviour
     between the two modules.

     Simplify the C# and Java statistics reporting methods.

Julien.

diff --git a/library/benchmarking.m b/library/benchmarking.m
index 0768d68c1..25097fd74 100644
--- a/library/benchmarking.m
+++ b/library/benchmarking.m
@@ -173,68 +173,18 @@
  %---------------------%

  report_stats :-
-    impure do_report_stats(io.stderr_stream).
-
-:- impure pred do_report_stats(io.text_output_stream::in) is det.
-
-:- pragma foreign_proc("C",
-    do_report_stats(S::in),
-    [will_not_call_mercury],
-"
-    MercuryFile mf = *(MR_unwrap_output_stream(S));
-    MR_report_standard_stats(MR_file(mf), &MR_line_number(mf));
-").
-
-:- pragma foreign_proc("C#",
-    do_report_stats(Stream::in),
-    [may_call_mercury, terminates],
-"
-    // The field F1 is the first field of io.output_stream/1
-    // functor.
-    ML_report_standard_stats(Stream.F1);
-").
-
-:- pragma foreign_proc("Java",
-    do_report_stats(Stream::in),
-    [may_call_mercury, terminates],
-"
-    // The field F1 is the first field of io.output_stream/1
-    // functor.  The downcast is required because streams are
-    // passed around as MR_MercuryFileStructs.
-    ML_report_standard_stats((io.MR_TextOutputFile) Stream.F1);
-").
+    trace [io(!IO)] (
+        io.report_standard_stats(!IO)
+    ),
+    impure impure_true.

  %---------------------%

  report_full_memory_stats :-
-    impure do_report_full_memory_stats(io.stderr_stream).
-
-:- impure pred do_report_full_memory_stats(io.text_output_stream::in) is det.
-
-:- pragma foreign_proc("C",
-    do_report_full_memory_stats(Stream::in),
-    [will_not_call_mercury],
-"
-    MercuryFile mf = *(MR_unwrap_output_stream(Stream));
-    MR_report_full_memory_stats(MR_file(mf), &MR_line_number(mf));
-").
-
-:- pragma foreign_proc("C#",
-    do_report_full_memory_stats(Stream::in),
-    [will_not_call_mercury],
-"
-    // See above for an explanation of what F1 does.
-    ML_report_full_memory_stats(Stream.F1);
-").
-
-:- pragma foreign_proc("Java",
-    do_report_full_memory_stats(Stream::in),
-    [will_not_call_mercury],
-"
-    // See above for an explanation of what F1 does and why
-    // the downcast is necessary here.
-    ML_report_full_memory_stats((io.MR_TextOutputFile) Stream.F1);
-").
+    trace [io(!IO)] (
+        io.report_full_memory_stats(!IO)
+    ),
+    impure impure_true.

  %---------------------------------------------------------------------------%

@@ -288,21 +238,17 @@ ML_report_standard_stats(io.MR_MercuryFileStruct stream)
      long real_time_at_prev_stat = real_time_at_last_stat;
      real_time_at_last_stat = System.DateTime.Now.Ticks;

-    try {
-        io.mercury_print_string(stream, System.String.Format(
-            ""[User time: +{0:F2}s, {1:F2}s Real time: +{2:F2}s, {3:F2}s]\\n"",
-            (user_time_at_last_stat - user_time_at_prev_stat),
-            (user_time_at_last_stat - user_time_at_start),
-            ((real_time_at_last_stat - real_time_at_prev_stat)
-                / (double) System.TimeSpan.TicksPerSecond),
-            ((real_time_at_last_stat - real_time_at_start)
-                / (double) System.TimeSpan.TicksPerSecond)
-        ));
-        // XXX At this point there should be a whole bunch of memory usage
-        // statistics.
-    } catch (System.SystemException e) {
-        // XXX how should we handle I/O errors when printing statistics?
-    }
+    io.mercury_print_string(stream, System.String.Format(
+        ""[User time: +{0:F2}s, {1:F2}s Real time: +{2:F2}s, {3:F2}s]\\n"",
+        (user_time_at_last_stat - user_time_at_prev_stat),
+        (user_time_at_last_stat - user_time_at_start),
+        ((real_time_at_last_stat - real_time_at_prev_stat)
+            / (double) System.TimeSpan.TicksPerSecond),
+        ((real_time_at_last_stat - real_time_at_start)
+           / (double) System.TimeSpan.TicksPerSecond)
+    ));
+    // XXX At this point there should be a whole bunch of memory usage
+    // statistics.
  }

  public static void
@@ -311,14 +257,9 @@ ML_report_full_memory_stats(io.MR_MercuryFileStruct stream)
      // XXX The support for this predicate is even worse. Since we don't have
      // access to memory usage statistics, all you get here is an apology.
      // But at least it doesn't just crash with an error.
-
-    try {
-        io.mercury_print_string(stream,
-            ""Sorry, report_full_memory_stats is not yet "" +
+    io.mercury_print_string(stream,
+        ""Sorry, report_full_memory_stats is not yet "" +
              ""implemented for the C# back-end.\\n"");
-    } catch (System.SystemException e) {
-        // XXX how should we handle I/O errors when printing statistics?
-    }
  }
  ").

@@ -341,6 +282,7 @@ ML_initialise()

  public static void
  ML_report_standard_stats(jmercury.io.MR_TextOutputFile stream)
+    throws java.io.IOException
  {
      int user_time_at_prev_stat = user_time_at_last_stat;
      user_time_at_last_stat = ML_get_user_cpu_milliseconds();
@@ -348,44 +290,35 @@ ML_report_standard_stats(jmercury.io.MR_TextOutputFile stream)
      long real_time_at_prev_stat = real_time_at_last_stat;
      real_time_at_last_stat = System.currentTimeMillis();

-    try {
-        stream.write(
-            ""[User time: +"" +
-            ((user_time_at_last_stat - user_time_at_prev_stat) / 1000.0) +
-            ""s, "" +
-            ((user_time_at_last_stat - user_time_at_start) / 1000.0) +
-            ""s"");
-
-        stream.write(
-            "" Real time: +"" +
-            ((real_time_at_last_stat - real_time_at_prev_stat) / 1000.0) +
-            ""s, "" +
-            ((real_time_at_last_stat - real_time_at_start) / 1000.0) +
-            ""s"");
-
-        // XXX At this point there should be a whole bunch of memory usage
-        // statistics. Unfortunately the Java back-end does not yet support
-        // this amount of profiling, so cpu time is all you get.
-
-        stream.write(""]\\n"");
-    } catch (java.io.IOException e) {
-        // XXX how should we handle I/O errors when printing statistics?
-    }
+    stream.write(
+        ""[User time: +"" +
+        ((user_time_at_last_stat - user_time_at_prev_stat) / 1000.0) +
+        ""s, "" +
+        ((user_time_at_last_stat - user_time_at_start) / 1000.0) +
+        ""s"");
+
+    stream.write(
+        "" Real time: +"" +
+        ((real_time_at_last_stat - real_time_at_prev_stat) / 1000.0) +
+        ""s, "" +
+        ((real_time_at_last_stat - real_time_at_start) / 1000.0) +
+        ""s"");
+
+    // XXX At this point there should be a whole bunch of memory usage
+    // statistics. Unfortunately the Java back-end does not yet support
+    // this amount of profiling, so cpu time is all you get.
  }

  public static void
  ML_report_full_memory_stats(jmercury.io.MR_TextOutputFile stream)
+    throws java.io.IOException
  {
      // XXX The support for this predicate is even worse. Since we don't have
      // access to memory usage statistics, all you get here is an apology.
      // But at least it doesn't just crash with an error.

-    try {
-        stream.write(""Sorry, report_full_memory_stats is not yet "" +
-            ""implemented for the Java back-end."");
-    } catch (java.io.IOException e) {
-        // XXX how should we handle I/O errors when printing statistics?
-    }
+    stream.write(""Sorry, report_full_memory_stats is not yet "" +
+        ""implemented for the Java back-end."");
  }
  ").

diff --git a/library/io.m b/library/io.m
index 7132df86f..8444a8979 100644
--- a/library/io.m
+++ b/library/io.m
@@ -12110,69 +12110,95 @@ report_stats(Selector, !IO) :-
  %---------------------%

  report_standard_stats(output_stream(Stream), !IO) :-
-    report_standard_stats_2(Stream, !IO).
+    report_standard_stats_2(Stream, Error, !IO),
+    throw_on_output_error(Error, !IO).

  report_standard_stats(!IO) :-
      io.stderr_stream(StdErr, !IO),
      report_standard_stats(StdErr, !IO).

-:- pred report_standard_stats_2(io.stream::in, io::di, io::uo) is det.
+:- pred report_standard_stats_2(io.stream::in, system_error::out,
+    io::di, io::uo) is det.

  :- pragma foreign_proc("C",
-    report_standard_stats_2(Stream::in, _IO0::di, _IO::uo),
+    report_standard_stats_2(Stream::in, Error::out, _IO0::di, _IO::uo),
      [promise_pure, will_not_call_mercury, tabled_for_io,
          does_not_affect_liveness],
  "
-    MR_report_standard_stats(MR_file(*Stream), &MR_line_number(*Stream));
+    Error = MR_report_standard_stats(MR_file(*Stream),
+        &MR_line_number(*Stream));
  ").

  :- pragma foreign_proc("C#",
-    report_standard_stats_2(Stream::in, _IO0::di, _IO::uo),
+    report_standard_stats_2(Stream::in, Error::out, _IO0::di, _IO::uo),
      [promise_pure, will_not_call_mercury],
  "
-    benchmarking.ML_report_standard_stats(Stream);
+    try {
+        benchmarking.ML_report_standard_stats(Stream);
+        Error = null;
+    } catch (System.SystemException e) {
+        Error = e;
+    }
  ").

  :- pragma foreign_proc("Java",
-    report_standard_stats_2(Stream::in, _IO0::di, _IO::uo),
+    report_standard_stats_2(Stream::in, Error::out, _IO0::di, _IO::uo),
      [promise_pure, will_not_call_mercury],
  "
-    jmercury.benchmarking.ML_report_standard_stats(
-        (jmercury.io.MR_TextOutputFile) Stream);
+    try {
+        jmercury.benchmarking.ML_report_standard_stats(
+            (jmercury.io.MR_TextOutputFile) Stream);
+        Error = null;
+    } catch (java.io.IOException e) {
+        Error = e;
+    }
  ").

  %---------------------%

  report_full_memory_stats(output_stream(Stream), !IO) :-
-    report_full_memory_stats_2(Stream, !IO).
+    report_full_memory_stats_2(Stream, Error, !IO),
+    throw_on_output_error(Error, !IO).

  report_full_memory_stats(!IO) :-
      io.stderr_stream(StdErr, !IO),
      report_full_memory_stats(StdErr, !IO).

-:- pred report_full_memory_stats_2(io.stream::in, io::di, io::uo) is det.
+:- pred report_full_memory_stats_2(io.stream::in, system_error::out,
+    io::di, io::uo) is det.

  :- pragma foreign_proc("C",
-    report_full_memory_stats_2(Stream::in, _IO0::di, _IO::uo),
+    report_full_memory_stats_2(Stream::in, Error::out, _IO0::di, _IO::uo),
      [promise_pure, will_not_call_mercury, tabled_for_io,
          does_not_affect_liveness],
  "
-    MR_report_full_memory_stats(MR_file(*Stream), &MR_line_number(*Stream));
+    Error = MR_report_full_memory_stats(MR_file(*Stream),
+        &MR_line_number(*Stream));
  ").

  :- pragma foreign_proc("C#",
-    report_full_memory_stats_2(Stream::in, _IO0::di, _IO::uo),
+    report_full_memory_stats_2(Stream::in, Error::out, _IO0::di, _IO::uo),
      [promise_pure, will_not_call_mercury],
  "
-    benchmarking.ML_report_full_memory_stats(Stream);
+    try {
+        benchmarking.ML_report_full_memory_stats(Stream);
+        Error = null;
+    } catch (System.SystemException e) {
+        Error = e;
+    }
  ").

  :- pragma foreign_proc("Java",
-    report_full_memory_stats_2(Stream::in, _IO0::di, _IO::uo),
+    report_full_memory_stats_2(Stream::in, Error::out, _IO0::di, _IO::uo),
      [promise_pure, will_not_call_mercury],
  "
-    jmercury.benchmarking.ML_report_full_memory_stats(
-        (jmercury.io.MR_TextOutputFile) Stream);
+    try {
+        jmercury.benchmarking.ML_report_full_memory_stats(
+            (jmercury.io.MR_TextOutputFile) Stream);
+        Error = null;
+    } catch (java.io.IOException e) {
+        Error = e;
+    }
  ").

  %---------------------%
diff --git a/runtime/mercury_report_stats.c b/runtime/mercury_report_stats.c
index 0efa0bad1..73888f34f 100644
--- a/runtime/mercury_report_stats.c
+++ b/runtime/mercury_report_stats.c
@@ -10,6 +10,7 @@
  ////////////////////////////////////////////////////////////////////////////

  #include <stdlib.h>
+#include <errno.h>
  #include "mercury_prof_mem.h"
  #include "mercury_heap_profile.h"
  #include "mercury_wrapper.h"        // for MR_user_time_at_last_stat
@@ -57,7 +58,7 @@
    static    int     MR_memory_profile_fill_table(MR_memprof_record *node,
                          MR_memprof_report_entry *table, int next_slot);

-  static    void    MR_memory_profile_report(FILE *fp, int *line_number,
+  static    int     MR_memory_profile_report(FILE *fp, int *line_number,
                          const MR_memprof_report_entry *,
                          int num_entries, MR_bool complete);

@@ -66,7 +67,7 @@

  #endif // MR_MPROF_PROFILE_MEMORY

-void
+int
  MR_report_standard_stats(FILE *fp, int *line_number)
  {
      int                 user_time_at_prev_stat;
@@ -78,6 +79,7 @@ MR_report_standard_stats(FILE *fp, int *line_number)
      int                 num_table_entries;
      MR_memprof_report_entry table[MEMORY_PROFILE_SIZE];
  #endif
+    int                 profile_report_result;

      // Print timing and stack usage information.

@@ -91,23 +93,35 @@ MR_report_standard_stats(FILE *fp, int *line_number)
      eng = MR_get_engine();
  #endif

-    fprintf(fp, "[User time: +%.3fs, %.3fs,",
-        (MR_user_time_at_last_stat - user_time_at_prev_stat) / 1000.0,
-        (MR_user_time_at_last_stat - MR_user_time_at_start) / 1000.0
-    );
+    if (
+        fprintf(fp, "[User time: +%.3fs, %.3fs,",
+            (MR_user_time_at_last_stat - user_time_at_prev_stat) / 1000.0,
+            (MR_user_time_at_last_stat - MR_user_time_at_start) / 1000.0
+        ) < 0
+    ) {
+        return errno;
+    }

-    fprintf(fp, " Real time: +%.3fs, %.3fs,",
-        (MR_real_time_at_last_stat - real_time_at_prev_stat) / 1000.0,
-        (MR_real_time_at_last_stat - MR_real_time_at_start) / 1000.0
-    );
+    if (
+        fprintf(fp, " Real time: +%.3fs, %.3fs,",
+            (MR_real_time_at_last_stat - real_time_at_prev_stat) / 1000.0,
+            (MR_real_time_at_last_stat - MR_real_time_at_start) / 1000.0
+        ) < 0
+    ) {
+        return errno;
+    }

  #ifndef MR_HIGHLEVEL_CODE
-    fprintf(fp, " D Stack: %.3fk, ND Stack: %.3fk,",
-        ((char *) MR_sp - (char *)
-            eng->MR_eng_context.MR_ctxt_detstack_zone->MR_zone_min) / 1024.0,
-        ((char *) MR_maxfr - (char *)
-            eng->MR_eng_context.MR_ctxt_nondetstack_zone->MR_zone_min) / 1024.0
-    );
+    if (
+        fprintf(fp, " D Stack: %.3fk, ND Stack: %.3fk,",
+            ((char *) MR_sp - (char *)
+                eng->MR_eng_context.MR_ctxt_detstack_zone->MR_zone_min) / 1024.0,
+            ((char *) MR_maxfr - (char *)
+                eng->MR_eng_context.MR_ctxt_nondetstack_zone->MR_zone_min) / 1024.0
+        ) < 0
+    ) {
+        return errno;
+    }
  #endif

  #ifdef MR_BOEHM_GC
@@ -116,25 +130,39 @@ MR_report_standard_stats(FILE *fp, int *line_number)
          struct GC_stack_base base;

          if (GC_SUCCESS == GC_get_stack_base(&base)) {
-            fprintf(fp, " C Stack: %.3fk,",
-                labs(&local_var - (char *)base.mem_base) / 1024.0);
+            if (
+                fprintf(fp, " C Stack: %.3fk,",
+                    labs(&local_var - (char *) base.mem_base) / 1024.0) < 0
+            ) {
+                return errno;
+            }
          } else {
-            fprintf(fp, " Cannot locate C stack base.");
+            if (fprintf(fp, " Cannot locate C stack base.") < 0) {
+                return errno;
+            }
          }
      }
  #endif

  #ifdef MR_USE_TRAIL
      #ifdef MR_THREAD_SAFE
-        fprintf(fp, ", Trail: %.3fk,",
-            ((char *) MR_trail_ptr -
-            (char *) MR_CONTEXT(MR_ctxt_trail_zone)->MR_zone_min) / 1024.0
-        );
+        if (
+            fprintf(fp, ", Trail: %.3fk,",
+                ((char *) MR_trail_ptr -
+                (char *) MR_CONTEXT(MR_ctxt_trail_zone)->MR_zone_min) / 1024.0
+            ) < 0
+        ) {
+            return errno;
+        }
      #else
-        fprintf(fp, " Trail: %.3fk,",
-            ((char *) MR_trail_ptr -
-            (char *) MR_trail_zone->MR_zone_min) / 1024.0
-        );
+        if (
+            fprintf(fp, " Trail: %.3fk,",
+                ((char *) MR_trail_ptr -
+                (char *) MR_trail_zone->MR_zone_min) / 1024.0
+            ) < 0
+        ) {
+            return errno;
+        }
     #endif // !MR_THREAD_SAFE
  #endif // !MR_USE_TRAIL

@@ -142,23 +170,35 @@ MR_report_standard_stats(FILE *fp, int *line_number)

  #ifdef MR_CONSERVATIVE_GC
    #ifdef MR_BOEHM_GC
-    fprintf(fp, "\n#GCs: %lu, ",
-        (unsigned long) GC_get_gc_no());
+    if (fprintf(fp, "\n#GCs: %lu, ", (unsigned long) GC_get_gc_no()) < 0) {
+        return errno;
+    }
      if (GC_mercury_calc_gc_time) {
          // Convert from unsigned long milliseconds to float seconds.
-        fprintf(fp, "total GC time: %.2fs, ",
-            (float) GC_total_gc_time / (float) 1000);
+        if (fprintf(fp, "total GC time: %.2fs, ",
+            (float) GC_total_gc_time / (float) 1000) < 0
+        ) {
+            return errno;
+        }
+    }
+    if (
+        fprintf(fp, "Heap used since last GC: %.3fk, Total used: %.3fk",
+            GC_get_bytes_since_gc() / 1024.0,
+            GC_get_heap_size() / 1024.0
+        ) < 0
+    ) {
+        return errno;
      }
-    fprintf(fp, "Heap used since last GC: %.3fk, Total used: %.3fk",
-        GC_get_bytes_since_gc() / 1024.0,
-        GC_get_heap_size() / 1024.0
-    );
      (*line_number)++;
    #endif
  #else // !MR_CONSERVATIVE_GC
-    fprintf(fp, "\nHeap: %.3fk",
-        ((char *) MR_hp - (char *) eng->MR_eng_heap_zone->MR_zone_min) / 1024.0
-    );
+    if (
+        fprintf(fp, "\nHeap: %.3fk",
+            ((char *) MR_hp - (char *) eng->MR_eng_heap_zone->MR_zone_min) / 1024.0
+        ) < 0)
+    {
+        return errno;
+    }
      (*line_number)++;
  #endif // !MR_CONSERVATIVE_GC

@@ -171,45 +211,66 @@ MR_report_standard_stats(FILE *fp, int *line_number)
      // Print out the per-procedure memory profile (top N entries).
      num_table_entries = MR_memory_profile_top_table(MR_memprof_procs.root,
          table, MEMORY_PROFILE_SIZE, 0);
-    fprintf(fp, "\nMemory profile by procedure\n");
+    if (fprintf(fp, "\nMemory profile by procedure\n") < 0) {
+        return errno;
+    }
      *line_number += 2;
-    MR_memory_profile_report(fp, line_number, table, num_table_entries,
-        MR_FALSE);
+    profile_report_result = MR_memory_profile_report(fp, line_number, table,
+        num_table_entries, MR_FALSE);
+    if (profile_report_result != 0) {
+        return profile_report_result;
+    }

      // Print out the per-type memory profile (top N entries).
      num_table_entries = MR_memory_profile_top_table(MR_memprof_types.root,
          table, MEMORY_PROFILE_SIZE, 0);
-    fprintf(fp, "\nMemory profile by type\n");
+    if (fprintf(fp, "\nMemory profile by type\n") < 0) {
+        return errno;
+    }
      *line_number += 2;
-    MR_memory_profile_report(fp, line_number, table, num_table_entries,
-        MR_FALSE);
+    profile_report_result = MR_memory_profile_report(fp, line_number, table,
+        num_table_entries, MR_FALSE);
+    if (profile_report_result != 0) {
+        return profile_report_result;
+    }

      // Print out the overall memory usage.
-    fprintf(fp, "Overall memory usage:"
-        "+%8.8g %8.8g cells, +%8.8g %8.8g words\n",
-        MR_overall_memprof_counter.cells_since_period_start,
-        MR_overall_memprof_counter.cells_at_period_end,
-        MR_overall_memprof_counter.words_since_period_start,
-        MR_overall_memprof_counter.words_at_period_end
-    );
+    if (
+        fprintf(fp, "Overall memory usage:"
+            "+%8.8g %8.8g cells, +%8.8g %8.8g words\n",
+            MR_overall_memprof_counter.cells_since_period_start,
+            MR_overall_memprof_counter.cells_at_period_end,
+            MR_overall_memprof_counter.words_since_period_start,
+            MR_overall_memprof_counter.words_at_period_end
+        ) < 0
+    ) {
+        return errno;
+    }
      (*line_number)++;

  #endif // MR_MPROF_PROFILE_MEMORY

-    fprintf(fp, "]\n");
+    if (fprintf(fp, "]\n") < 0) {
+        return errno;
+    }
      (*line_number)++;
+
+    return 0;
  }

-void
+int
  MR_report_full_memory_stats(FILE *fp, int *line_number)
  {
  #ifndef MR_MPROF_PROFILE_MEMORY
-    fprintf(fp, "\nMemory profiling is not enabled.\n");
+    if (fprintf(fp, "\nMemory profiling is not enabled.\n") < 0) {
+        return errno;
+    }
      *line_number += 2;
  #else
      int                     num_table_entries;
      int                     table_size;
      MR_memprof_report_entry *table;
+    int                     profile_report_result;

      // Update the overall counter (this needs to be done first,
      // so that the percentages come out right).
@@ -228,37 +289,60 @@ MR_report_full_memory_stats(FILE *fp, int *line_number)
          table, 0);
      qsort(table, MR_memprof_procs.num_entries, sizeof(MR_memprof_report_entry),
          MR_memory_profile_compare_final);
-    fprintf(fp, "\nMemory profile by procedure\n");
+    if (fprintf(fp, "\nMemory profile by procedure\n") < 0) {
+        return errno;
+    }
      *line_number += 2;
-    fprintf(fp, "%14s %14s  %s\n",
-        "Cells", "Words", "Procedure label");
+    if (fprintf(fp, "%14s %14s  %s\n",
+            "Cells", "Words", "Procedure label") < 0
+    ) {
+        return errno;
+    }
      (*line_number)++;
-    MR_memory_profile_report(fp, line_number, table, num_table_entries,
-        MR_TRUE);
+    profile_report_result = MR_memory_profile_report(fp, line_number,
+        table, num_table_entries, MR_TRUE);
+    if (profile_report_result != 0) {
+        return profile_report_result;
+    }

      // Print the by-type memory profile.
      num_table_entries = MR_memory_profile_fill_table(MR_memprof_types.root,
          table, 0);
      qsort(table, MR_memprof_types.num_entries, sizeof(MR_memprof_report_entry),
          MR_memory_profile_compare_final);
-    fprintf(fp, "\nMemory profile by type\n");
+    if (fprintf(fp, "\nMemory profile by type\n") < 0) {
+        return errno;
+    }
      *line_number += 2;
-    fprintf(fp, "%14s %14s  %s\n",
-        "Cells", "Words", "Procedure label");
+    if (
+        fprintf(fp, "%14s %14s  %s\n",
+            "Cells", "Words", "Procedure label") < 0
+    ) {
+        return errno;
+    }
      (*line_number)++;
-    MR_memory_profile_report(fp, line_number, table, num_table_entries,
-        MR_TRUE);
+    profile_report_result = MR_memory_profile_report(fp, line_number, table,
+        num_table_entries, MR_TRUE);
+    if (profile_report_result != 0) {
+        return profile_report_result;
+    }

      // Deallocate space for the table.
      MR_GC_free(table);

      // Print the overall memory usage.
-    fprintf(fp, "\nOverall memory usage: %8.8g cells, %8.8g words\n",
-        MR_overall_memprof_counter.cells_at_period_end,
-        MR_overall_memprof_counter.words_at_period_end
-    );
+    if (
+        fprintf(fp, "\nOverall memory usage: %8.8g cells, %8.8g words\n",
+            MR_overall_memprof_counter.cells_at_period_end,
+            MR_overall_memprof_counter.words_at_period_end
+        ) < 0
+    ) {
+        return errno;
+    }
      *line_number += 2;
  #endif // MR_MPROF_PROFILE_MEMORY
+
+    return 0;
  }

  #ifdef MR_MPROF_PROFILE_MEMORY
@@ -413,8 +497,9 @@ MR_memory_profile_fill_table(MR_memprof_record *node,
  // MR_memory_profile_report(fp, line_number, table, num_entries, complete):
  //
  // Print out a profiling report for the specified table.
+// Returns zero on success, or errno if an error occurs.

-static void
+static int
  MR_memory_profile_report(FILE *fp, int *line_number,
      const MR_memprof_report_entry *table, int num_entries, MR_bool complete)
  {
@@ -425,17 +510,21 @@ MR_memory_profile_report(FILE *fp, int *line_number,
          if (MR_overall_memprof_counter.cells_at_period_end < 1.0
          ||  MR_overall_memprof_counter.words_at_period_end < 1.0)
          {
-            fprintf(fp, "no allocations to report\n");
+            if (fprintf(fp, "no allocations to report\n") < 0) {
+                return errno;
+            }
              (*line_number)++;
-            return;
+            return 0;
          }
      } else {
          if (MR_overall_memprof_counter.cells_since_period_start < 1.0
          ||  MR_overall_memprof_counter.words_since_period_start < 1.0)
          {
-            fprintf(fp, "no allocations to report\n");
+            if (fprintf(fp, "no allocations to report\n") < 0) {
+                return errno;
+            }
              (*line_number)++;
-            return;
+            return 0;
          }
      }

@@ -445,29 +534,39 @@ MR_memory_profile_report(FILE *fp, int *line_number,

      for (i = 0; i < num_entries; i++) {
          if (complete) {
-            fprintf(fp, "%8.8g/%4.1f%% %8.8g/%4.1f%%  %s\n",
-                table[i].counter.cells_at_period_end,
-                100 * table[i].counter.cells_at_period_end /
-                    MR_overall_memprof_counter.cells_at_period_end,
-                table[i].counter.words_at_period_end,
-                100 * table[i].counter.words_at_period_end /
-                    MR_overall_memprof_counter.words_at_period_end,
-                table[i].name
-            );
+            if (
+                fprintf(fp, "%8.8g/%4.1f%% %8.8g/%4.1f%%  %s\n",
+                    table[i].counter.cells_at_period_end,
+                    100 * table[i].counter.cells_at_period_end /
+                        MR_overall_memprof_counter.cells_at_period_end,
+                    table[i].counter.words_at_period_end,
+                    100 * table[i].counter.words_at_period_end /
+                        MR_overall_memprof_counter.words_at_period_end,
+                    table[i].name
+                ) < 0
+            ) {
+                return errno;
+            }
              (*line_number)++;
          } else {
-            fprintf(fp, "%8.8g/%4.1f%% %8.8g/%4.1f%%  %s\n",
-                table[i].counter.cells_since_period_start,
-                100 * table[i].counter.cells_since_period_start /
-                   MR_overall_memprof_counter.cells_since_period_start,
-                table[i].counter.words_since_period_start,
-                100 * table[i].counter.words_since_period_start /
-                   MR_overall_memprof_counter.words_since_period_start,
-                table[i].name
-            );
+            if (
+                fprintf(fp, "%8.8g/%4.1f%% %8.8g/%4.1f%%  %s\n",
+                    table[i].counter.cells_since_period_start,
+                    100 * table[i].counter.cells_since_period_start /
+                        MR_overall_memprof_counter.cells_since_period_start,
+                    table[i].counter.words_since_period_start,
+                    100 * table[i].counter.words_since_period_start /
+                        MR_overall_memprof_counter.words_since_period_start,
+                    table[i].name
+                ) < 0
+            ) {
+                return errno;
+            }
              (*line_number)++;
          }
      }
+
+    return 0;
  }

  // Comparison routine used for qsort().
diff --git a/runtime/mercury_report_stats.h b/runtime/mercury_report_stats.h
index 3ffdcbdfc..a1773a452 100644
--- a/runtime/mercury_report_stats.h
+++ b/runtime/mercury_report_stats.h
@@ -13,8 +13,8 @@
  #include "mercury_timing.h"
  #include "mercury_heap.h"

-extern void MR_report_standard_stats(FILE *fp, int *line_number);
+extern int MR_report_standard_stats(FILE *fp, int *line_number);

-extern void MR_report_full_memory_stats(FILE *fp, int *line_number);
+extern int MR_report_full_memory_stats(FILE *fp, int *line_number);

  #endif  // not MERCURY_REPORT_STATS_H


More information about the reviews mailing list