Blob Blame History Raw
From dac12e5db7f4ca4a32b0eccea1d16d27f9df86d2 Mon Sep 17 00:00:00 2001
From: Stephen Dolan <sdolan@janestreet.com>
Date: Tue, 16 Jul 2019 16:24:01 +0100
Subject: [PATCH 6/8] Retain debug information about allocation sizes, for
 statmemprof.

This code is adapted from jhjourdan's 2c93ca1e711. Comballoc is
extended to keep track of allocation sizes and debug info for each
allocation, and the frame table format is modified to store them.

The native code GC-entry logic is changed to match bytecode, by
calling the garbage collector at most once per allocation.

amd64 only, for now.

(cherry picked from commit 34f97941ec302129f516c926c9ef65e4d68b8121)
---
 asmcomp/amd64/emit.mlp         | 30 ++++++++-----------
 asmcomp/comballoc.ml           | 55 +++++++++++++++++++---------------
 asmcomp/emitaux.ml             | 22 ++++++++++++++
 asmcomp/emitaux.mli            |  1 +
 asmcomp/mach.ml                |  6 +++-
 asmcomp/mach.mli               | 11 ++++++-
 asmcomp/selectgen.ml           |  8 +++--
 asmcomp/spacetime_profiling.ml |  1 +
 runtime/amd64.S                | 53 ++++----------------------------
 runtime/backtrace_nat.c        | 16 ++++++++--
 runtime/caml/stack.h           | 10 +++++--
 runtime/minor_gc.c             |  5 ++++
 runtime/roots_nat.c            |  5 ++++
 runtime/signals_nat.c          | 37 +++++++++++------------
 14 files changed, 143 insertions(+), 117 deletions(-)

diff --git a/asmcomp/amd64/emit.mlp b/asmcomp/amd64/emit.mlp
index 69cc48b6d..6c3950a6d 100644
--- a/asmcomp/amd64/emit.mlp
+++ b/asmcomp/amd64/emit.mlp
@@ -299,13 +299,7 @@ let emit_call_gc gc =
     assert Config.spacetime;
     spacetime_before_uninstrumented_call ~node_ptr ~index
   end;
-  begin match gc.gc_size with
-  | 16 -> emit_call "caml_call_gc1"
-  | 24 -> emit_call "caml_call_gc2"
-  | 32 -> emit_call "caml_call_gc3"
-  | n ->  I.add (int n) r15;
-          emit_call "caml_call_gc"
-  end;
+  emit_call "caml_call_gc";
   def_label gc.gc_frame;
   I.jmp (label gc.gc_return_lbl)
 
@@ -667,21 +661,21 @@ let emit_instr fallthrough i =
       | Double | Double_u ->
           I.movsd (arg i 0) (addressing addr REAL8 i 1)
       end
-  | Lop(Ialloc { bytes = n; label_after_call_gc; spacetime_index; }) ->
-    if !fastcode_flag then begin
-        let lbl_redo = new_label() in
-        def_label lbl_redo;
+  | Lop(Ialloc { bytes = n; label_after_call_gc; spacetime_index; dbginfo }) ->
+      let dbginfo =
+        if not !Clflags.debug && not Config.spacetime then
+          List.map (fun d -> { d with alloc_dbg = Debuginfo.none }) dbginfo
+        else dbginfo in
+      if !fastcode_flag then begin
         I.sub (int n) r15;
         I.cmp (domain_field Domainstate.Domain_young_limit) r15;
         let lbl_call_gc = new_label() in
-        let dbg =
-          if not Config.spacetime then Debuginfo.none
-          else i.dbg
-        in
         let lbl_frame =
-          record_frame_label ?label:label_after_call_gc i.live (Dbg_other dbg)
+          record_frame_label ?label:label_after_call_gc i.live (Dbg_alloc dbginfo)
         in
         I.jb (label lbl_call_gc);
+        let lbl_after_alloc = new_label() in
+        def_label lbl_after_alloc;
         I.lea (mem64 NONE 8 R15) (res i 0);
         let gc_spacetime =
           if not Config.spacetime then None
@@ -690,7 +684,7 @@ let emit_instr fallthrough i =
         call_gc_sites :=
           { gc_size = n;
             gc_lbl = lbl_call_gc;
-            gc_return_lbl = lbl_redo;
+            gc_return_lbl = lbl_after_alloc;
             gc_frame = lbl_frame;
             gc_spacetime; } :: !call_gc_sites
       end else begin
@@ -708,7 +702,7 @@ let emit_instr fallthrough i =
         end;
         let label =
           record_frame_label ?label:label_after_call_gc i.live
-            (Dbg_other i.dbg)
+            (Dbg_alloc dbginfo)
         in
         def_label label;
         I.lea (mem64 NONE 8 R15) (res i 0)
diff --git a/asmcomp/comballoc.ml b/asmcomp/comballoc.ml
index 29ee15b36..b8ebcf374 100644
--- a/asmcomp/comballoc.ml
+++ b/asmcomp/comballoc.ml
@@ -17,34 +17,41 @@
 
 open Mach
 
+type pending_alloc =
+  { reg: Reg.t;       (* register holding the result of the last allocation *)
+    dbginfos: alloc_dbginfo list; (* debug info for each pending allocation *)
+    totalsz: int }                  (* amount to be allocated in this block *)
+
 type allocation_state =
     No_alloc
-  | Pending_alloc of
-    { reg: Reg.t;    (* register holding the result of the last allocation *)
-      totalsz: int } (* amount to be allocated in this block *)
-
-let allocated_size = function
-    No_alloc -> 0
-  | Pending_alloc {totalsz; _} -> totalsz
+  | Pending_alloc of pending_alloc
 
 let rec combine i allocstate =
   match i.desc with
     Iend | Ireturn | Iexit _ | Iraise _ ->
-      (i, allocated_size allocstate)
-  | Iop(Ialloc { bytes = sz; _ }) ->
+      (i, allocstate)
+  | Iop(Ialloc { bytes = sz; dbginfo; _ }) ->
       begin match allocstate with
-      | Pending_alloc {reg; totalsz}
+      | Pending_alloc {reg; dbginfos; totalsz}
           when totalsz + sz < Config.max_young_wosize * Arch.size_addr ->
          let (next, totalsz) =
            combine i.next
-             (Pending_alloc { reg = i.res.(0); totalsz = totalsz + sz }) in
+             (Pending_alloc { reg = i.res.(0);
+                              dbginfos = dbginfo @ dbginfos;
+                              totalsz = totalsz + sz }) in
          (instr_cons_debug (Iop(Iintop_imm(Iadd, -sz)))
             [| reg |] i.res i.dbg next,
           totalsz)
       | No_alloc | Pending_alloc _ ->
-         let (next, totalsz) =
+         let (next, state) =
            combine i.next
-             (Pending_alloc { reg = i.res.(0); totalsz = sz }) in
+             (Pending_alloc { reg = i.res.(0);
+                              dbginfos = dbginfo;
+                              totalsz = sz }) in
+         let totalsz, dbginfo =
+           match state with
+           | No_alloc -> 0, dbginfo
+           | Pending_alloc { totalsz; dbginfos; _ } -> totalsz, dbginfos in
          let next =
            let offset = totalsz - sz in
            if offset = 0 then next
@@ -52,40 +59,40 @@ let rec combine i allocstate =
                 i.res i.dbg next
          in
          (instr_cons_debug (Iop(Ialloc {bytes = totalsz; spacetime_index = 0;
-                                        label_after_call_gc = None; }))
-          i.arg i.res i.dbg next, allocated_size allocstate)
+                                        dbginfo; label_after_call_gc = None; }))
+          i.arg i.res i.dbg next, allocstate)
       end
   | Iop(Icall_ind _ | Icall_imm _ | Iextcall _ |
         Itailcall_ind _ | Itailcall_imm _) ->
       let newnext = combine_restart i.next in
       (instr_cons_debug i.desc i.arg i.res i.dbg newnext,
-       allocated_size allocstate)
+       allocstate)
   | Iop _ ->
-      let (newnext, sz) = combine i.next allocstate in
-      (instr_cons_debug i.desc i.arg i.res i.dbg newnext, sz)
+      let (newnext, s') = combine i.next allocstate in
+      (instr_cons_debug i.desc i.arg i.res i.dbg newnext, s')
   | Iifthenelse(test, ifso, ifnot) ->
       let newifso = combine_restart ifso in
       let newifnot = combine_restart ifnot in
       let newnext = combine_restart i.next in
       (instr_cons (Iifthenelse(test, newifso, newifnot)) i.arg i.res newnext,
-       allocated_size allocstate)
+       allocstate)
   | Iswitch(table, cases) ->
       let newcases = Array.map combine_restart cases in
       let newnext = combine_restart i.next in
       (instr_cons (Iswitch(table, newcases)) i.arg i.res newnext,
-       allocated_size allocstate)
+       allocstate)
   | Icatch(rec_flag, handlers, body) ->
-      let (newbody, sz) = combine body allocstate in
+      let (newbody, s') = combine body allocstate in
       let newhandlers =
         List.map (fun (io, handler) -> io, combine_restart handler) handlers in
       let newnext = combine_restart i.next in
       (instr_cons (Icatch(rec_flag, newhandlers, newbody))
-         i.arg i.res newnext, sz)
+         i.arg i.res newnext, s')
   | Itrywith(body, handler) ->
-      let (newbody, sz) = combine body allocstate in
+      let (newbody, s') = combine body allocstate in
       let newhandler = combine_restart handler in
       let newnext = combine_restart i.next in
-      (instr_cons (Itrywith(newbody, newhandler)) i.arg i.res newnext, sz)
+      (instr_cons (Itrywith(newbody, newhandler)) i.arg i.res newnext, s')
 
 and combine_restart i =
   let (newi, _) = combine i No_alloc in newi
diff --git a/asmcomp/emitaux.ml b/asmcomp/emitaux.ml
index 9e7221096..8ed63af28 100644
--- a/asmcomp/emitaux.ml
+++ b/asmcomp/emitaux.ml
@@ -106,6 +106,7 @@ let emit_float32_directive directive x =
 (* Record live pointers at call points *)
 
 type frame_debuginfo =
+  | Dbg_alloc of Mach.alloc_dbginfo list
   | Dbg_raise of Debuginfo.t
   | Dbg_other of Debuginfo.t
 
@@ -173,6 +174,10 @@ let emit_frames a =
       match fd.fd_debuginfo with
       | Dbg_other d | Dbg_raise d ->
         if Debuginfo.is_none d then 0 else 1
+      | Dbg_alloc dbgs ->
+        if List.for_all (fun d ->
+          Debuginfo.is_none d.Mach.alloc_dbg) dbgs
+        then 2 else 3
     in
     a.efa_code_label fd.fd_lbl;
     a.efa_16 (fd.fd_frame_size + flags);
@@ -187,6 +192,23 @@ let emit_frames a =
     | Dbg_raise dbg ->
       a.efa_align 4;
       a.efa_label_rel (label_debuginfos true dbg) Int32.zero
+    | Dbg_alloc dbg ->
+      assert (List.length dbg < 256);
+      a.efa_8 (List.length dbg);
+      List.iter (fun Mach.{alloc_words;_} ->
+        (* Possible allocations range between 2 and 257 *)
+        assert (2 <= alloc_words &&
+                alloc_words - 1 <= Config.max_young_wosize &&
+                Config.max_young_wosize <= 256);
+        a.efa_8 (alloc_words - 2)) dbg;
+      if flags = 3 then begin
+        a.efa_align 4;
+        List.iter (fun Mach.{alloc_dbg; _} ->
+          if Debuginfo.is_none alloc_dbg then
+            a.efa_32 Int32.zero
+          else
+            a.efa_label_rel (label_debuginfos false alloc_dbg) Int32.zero) dbg
+      end
     end;
     a.efa_align Arch.size_addr
   in
diff --git a/asmcomp/emitaux.mli b/asmcomp/emitaux.mli
index a4a60e07c..1a8982a07 100644
--- a/asmcomp/emitaux.mli
+++ b/asmcomp/emitaux.mli
@@ -39,6 +39,7 @@ val emit_debug_info_gen :
   (file_num:int -> line:int -> col:int -> unit) -> unit
 
 type frame_debuginfo =
+  | Dbg_alloc of Mach.alloc_dbginfo list
   | Dbg_raise of Debuginfo.t
   | Dbg_other of Debuginfo.t
 
diff --git a/asmcomp/mach.ml b/asmcomp/mach.ml
index ab69e0ca3..8df94d039 100644
--- a/asmcomp/mach.ml
+++ b/asmcomp/mach.ml
@@ -39,6 +39,10 @@ type test =
   | Ioddtest
   | Ieventest
 
+type alloc_dbginfo =
+  { alloc_words : int;
+    alloc_dbg : Debuginfo.t }
+
 type operation =
     Imove
   | Ispill
@@ -55,7 +59,7 @@ type operation =
   | Iload of Cmm.memory_chunk * Arch.addressing_mode
   | Istore of Cmm.memory_chunk * Arch.addressing_mode * bool
   | Ialloc of { bytes : int; label_after_call_gc : label option;
-        spacetime_index : int; }
+      dbginfo : alloc_dbginfo list; spacetime_index : int; }
   | Iintop of integer_operation
   | Iintop_imm of integer_operation * int
   | Inegf | Iabsf | Iaddf | Isubf | Imulf | Idivf
diff --git a/asmcomp/mach.mli b/asmcomp/mach.mli
index 5df79585c..fd3e033bf 100644
--- a/asmcomp/mach.mli
+++ b/asmcomp/mach.mli
@@ -46,6 +46,15 @@ type test =
   | Ioddtest
   | Ieventest
 
+type alloc_dbginfo =
+  { alloc_words : int;
+    alloc_dbg : Debuginfo.t }
+(** Due to Comballoc, a single Ialloc instruction may combine several
+    unrelated allocations. Their Debuginfo.t (which may differ) are stored
+    as a list of alloc_dbginfo. This list is in order of increasing memory
+    address, which is the reverse of the original allocation order. Later
+    allocations are consed to the front of this list by Comballoc. *)
+
 type operation =
     Imove
   | Ispill
@@ -63,7 +72,7 @@ type operation =
   | Istore of Cmm.memory_chunk * Arch.addressing_mode * bool
                                  (* false = initialization, true = assignment *)
   | Ialloc of { bytes : int; label_after_call_gc : label option;
-      spacetime_index : int; }
+      dbginfo : alloc_dbginfo list; spacetime_index : int; }
     (** For Spacetime only, Ialloc instructions take one argument, being the
         pointer to the trie node for the current function. *)
   | Iintop of integer_operation
diff --git a/asmcomp/selectgen.ml b/asmcomp/selectgen.ml
index b024dfe7d..d5f54b699 100644
--- a/asmcomp/selectgen.ml
+++ b/asmcomp/selectgen.ml
@@ -419,7 +419,8 @@ method mark_instr = function
 (* Default instruction selection for operators *)
 
 method select_allocation bytes =
-  Ialloc { bytes; spacetime_index = 0; label_after_call_gc = None; }
+  Ialloc { bytes; label_after_call_gc = None;
+           dbginfo = []; spacetime_index = 0; }
 method select_allocation_args _env = [| |]
 
 method select_checkbound () =
@@ -775,8 +776,11 @@ method emit_expr (env:environment) exp =
           | Ialloc { bytes = _; spacetime_index; label_after_call_gc; } ->
               let rd = self#regs_for typ_val in
               let bytes = size_expr env (Ctuple new_args) in
+              assert (bytes mod Arch.size_addr = 0);
+              let alloc_words = bytes / Arch.size_addr in
               let op =
-                Ialloc { bytes; spacetime_index; label_after_call_gc; }
+                Ialloc { bytes; spacetime_index; label_after_call_gc;
+                         dbginfo = [{alloc_words; alloc_dbg = dbg}] }
               in
               let args = self#select_allocation_args env in
               self#insert_debug env (Iop op) dbg args rd;
diff --git a/asmcomp/spacetime_profiling.ml b/asmcomp/spacetime_profiling.ml
index a61cd1c43..62e182ab9 100644
--- a/asmcomp/spacetime_profiling.ml
+++ b/asmcomp/spacetime_profiling.ml
@@ -396,6 +396,7 @@ class virtual instruction_selection = object (self)
       in
       Mach.Ialloc {
         bytes;
+        dbginfo = [];
         label_after_call_gc = Some label;
         spacetime_index = index;
       }
diff --git a/runtime/amd64.S b/runtime/amd64.S
index 77a4f85aa..03c1f4e81 100644
--- a/runtime/amd64.S
+++ b/runtime/amd64.S
@@ -425,111 +425,70 @@ ENDFUNCTION(G(caml_call_gc))
 
 FUNCTION(G(caml_alloc1))
 CFI_STARTPROC
-LBL(caml_alloc1):
         subq    $16, %r15
         cmpq    Caml_state(young_limit), %r15
         jb      LBL(100)
         ret
 LBL(100):
-        addq    $16, %r15
         RECORD_STACK_FRAME(0)
         ENTER_FUNCTION
 /*        subq    $8, %rsp; CFI_ADJUST (8); */
         call    LBL(caml_call_gc)
 /*        addq    $8, %rsp; CFI_ADJUST (-8); */
         LEAVE_FUNCTION
-        jmp     LBL(caml_alloc1)
+        ret
 CFI_ENDPROC
 ENDFUNCTION(G(caml_alloc1))
 
 FUNCTION(G(caml_alloc2))
 CFI_STARTPROC
-LBL(caml_alloc2):
         subq    $24, %r15
         cmpq    Caml_state(young_limit), %r15
         jb      LBL(101)
         ret
 LBL(101):
-        addq    $24, %r15
         RECORD_STACK_FRAME(0)
         ENTER_FUNCTION
 /*        subq    $8, %rsp; CFI_ADJUST (8); */
         call    LBL(caml_call_gc)
 /*        addq    $8, %rsp; CFI_ADJUST (-8); */
         LEAVE_FUNCTION
-        jmp     LBL(caml_alloc2)
+        ret
 CFI_ENDPROC
 ENDFUNCTION(G(caml_alloc2))
 
 FUNCTION(G(caml_alloc3))
 CFI_STARTPROC
-LBL(caml_alloc3):
         subq    $32, %r15
         cmpq    Caml_state(young_limit), %r15
         jb      LBL(102)
         ret
 LBL(102):
-        addq    $32, %r15
         RECORD_STACK_FRAME(0)
         ENTER_FUNCTION
 /*        subq    $8, %rsp; CFI_ADJUST (8) */
         call    LBL(caml_call_gc)
 /*        addq    $8, %rsp; CFI_ADJUST (-8) */
         LEAVE_FUNCTION
-        jmp     LBL(caml_alloc3)
+        ret
 CFI_ENDPROC
 ENDFUNCTION(G(caml_alloc3))
 
 FUNCTION(G(caml_allocN))
 CFI_STARTPROC
-LBL(caml_allocN):
-        pushq   %rax; CFI_ADJUST(8)        /* save desired size */
         subq    %rax, %r15
         cmpq    Caml_state(young_limit), %r15
         jb      LBL(103)
-        addq    $8, %rsp; CFI_ADJUST (-8)  /* drop desired size */
         ret
 LBL(103):
-        addq    0(%rsp), %r15
-        CFI_ADJUST(8)
-        RECORD_STACK_FRAME(8)
-#ifdef WITH_FRAME_POINTERS
-        /* ensure 16 byte alignment by subq + enter using 16-bytes, PR#7417 */
-        subq    $8, %rsp; CFI_ADJUST (8)
+        RECORD_STACK_FRAME(0)
         ENTER_FUNCTION
-#endif
         call    LBL(caml_call_gc)
-#ifdef WITH_FRAME_POINTERS
-        /* ensure 16 byte alignment by leave + addq using 16-bytes PR#7417 */
         LEAVE_FUNCTION
-        addq    $8, %rsp; CFI_ADJUST (-8)
-#endif
-        popq    %rax; CFI_ADJUST(-8)       /* recover desired size */
-        jmp     LBL(caml_allocN)
+        ret
 CFI_ENDPROC
 ENDFUNCTION(G(caml_allocN))
-
-/* Reset the allocation pointer and invoke the GC */
-
-FUNCTION(G(caml_call_gc1))
-CFI_STARTPROC
-        addq    $16, %r15
-        jmp     GCALL(caml_call_gc)
-CFI_ENDPROC
-
-FUNCTION(G(caml_call_gc2))
-CFI_STARTPROC
-        addq    $24, %r15
-        jmp     GCALL(caml_call_gc)
-CFI_ENDPROC
-
-FUNCTION(G(caml_call_gc3))
-CFI_STARTPROC
-        addq    $32, %r15
-        jmp     GCALL(caml_call_gc)
-CFI_ENDPROC
-
-
+        
 /* Call a C function from OCaml */
 
 FUNCTION(G(caml_c_call))
diff --git a/runtime/backtrace_nat.c b/runtime/backtrace_nat.c
index acf31d644..ee18f2a00 100644
--- a/runtime/backtrace_nat.c
+++ b/runtime/backtrace_nat.c
@@ -146,8 +146,20 @@ debuginfo caml_debuginfo_extract(backtrace_slot slot)
   }
   /* Recover debugging info */
   infoptr = (unsigned char*)&d->live_ofs[d->num_live];
-  /* align to 32 bits */
-  infoptr = Align_to(infoptr, uint32_t);
+  if (d->frame_size & 2) {
+    /* skip alloc_lengths */
+    infoptr += *infoptr + 1;
+    /* align to 32 bits */
+    infoptr = Align_to(infoptr, uint32_t);
+    /* we know there's at least one valid debuginfo,
+       but it may not be the one for the first alloc */
+    while (*(uint32_t*)infoptr == 0) {
+      infoptr += sizeof(uint32_t);
+    }
+  } else {
+    /* align to 32 bits */
+    infoptr = Align_to(infoptr, uint32_t);
+  }
   /* read offset to debuginfo */
   debuginfo_offset = *(uint32_t*)infoptr;
   return (debuginfo)(infoptr + debuginfo_offset);
diff --git a/runtime/caml/stack.h b/runtime/caml/stack.h
index 30a18d274..44a881e41 100644
--- a/runtime/caml/stack.h
+++ b/runtime/caml/stack.h
@@ -89,9 +89,15 @@ typedef struct {
   unsigned short num_live;
   unsigned short live_ofs[1 /* num_live */];
   /*
+    If frame_size & 2, then allocation info follows:
+  unsigned char num_allocs;
+  unsigned char alloc_lengths[num_alloc];
+
     If frame_size & 1, then debug info follows:
-  uint32_t debug_info_offset;
-    Debug info is stored as a relative offset to a debuginfo structure. */
+  uint32_t debug_info_offset[num_debug];
+
+    Debug info is stored as relative offsets to debuginfo structures.
+    num_debug is num_alloc if frame_size & 2, otherwise 1. */
 } frame_descr;
 
 /* Used to compute offsets in frame tables.
diff --git a/runtime/minor_gc.c b/runtime/minor_gc.c
index e4dacfc51..4b3634275 100644
--- a/runtime/minor_gc.c
+++ b/runtime/minor_gc.c
@@ -509,6 +509,11 @@ void caml_alloc_small_dispatch (tag_t tag, intnat wosize, int flags)
        callbacks. */
     CAML_INSTR_INT ("force_minor/alloc_small@", 1);
     caml_gc_dispatch ();
+#if defined(NATIVE_CODE) && defined(WITH_SPACETIME)
+    if (caml_young_ptr == caml_young_alloc_end) {
+      caml_spacetime_automatic_snapshot();
+    }
+#endif
   }
 
   /* Re-do the allocation: we now have enough space in the minor heap. */
diff --git a/runtime/roots_nat.c b/runtime/roots_nat.c
index f61e56d90..b98555838 100644
--- a/runtime/roots_nat.c
+++ b/runtime/roots_nat.c
@@ -83,6 +83,11 @@ static frame_descr * next_frame_descr(frame_descr * d) {
   CAMLassert(d->retaddr >= 4096);
   /* Skip to end of live_ofs */
   p = (unsigned char*)&d->live_ofs[d->num_live];
+  /* Skip alloc_lengths if present */
+  if (d->frame_size & 2) {
+    num_allocs = *p;
+    p += num_allocs + 1;
+  }
   /* Skip debug info if present */
   if (d->frame_size & 1) {
     /* Align to 32 bits */
diff --git a/runtime/signals_nat.c b/runtime/signals_nat.c
index 017298394..075db46e3 100644
--- a/runtime/signals_nat.c
+++ b/runtime/signals_nat.c
@@ -69,29 +69,26 @@ extern char caml_system__code_begin, caml_system__code_end;
 
 void caml_garbage_collection(void)
 {
-  /* TEMPORARY: if we have just sampled an allocation in native mode,
-     we simply renew the sample to ignore it. Otherwise, renewing now
-     will not have any effect on the sampling distribution, because of
-     the memorylessness of the Bernoulli process.
-
-     FIXME: if the sampling rate is 1, this leads to infinite loop,
-     because we are using a binomial distribution in [memprof.c]. This
-     will go away when the sampling of natively allocated blocks will
-     be correctly implemented.
-  */
-  caml_memprof_renew_minor_sample();
-  if (Caml_state->requested_major_slice || Caml_state->requested_minor_gc ||
-      Caml_state->young_ptr - Caml_state->young_trigger < Max_young_whsize){
-    caml_gc_dispatch ();
+  frame_descr* d;
+  uintnat h;
+  h = Hash_retaddr(Caml_state->last_return_address);
+  while (1) {
+    d = caml_frame_descriptors[h];
+    if (d->retaddr == Caml_state->last_return_address) break;
+    h = (h + 1) & caml_frame_descriptors_mask;
   }
 
-#ifdef WITH_SPACETIME
-  if (Caml_state->young_ptr == Caml_state->young_alloc_end) {
-    caml_spacetime_automatic_snapshot();
-  }
-#endif
+  /* Must be an allocation frame */
+  CAMLassert(d && d->frame_size != 0xFFFF && (d->frame_size & 2));
+
+  unsigned char* alloc_len = (unsigned char*)(&d->live_ofs[d->num_live]);
+  int nallocs = *alloc_len++;
+  int allocsz = 0;
+  for (int i = 0; i < nallocs; i++) allocsz += alloc_len[i] + 2;
+  allocsz -= 1;
 
-  caml_raise_if_exception(caml_do_pending_actions_exn());
+  caml_alloc_small_dispatch(0 /* FIXME */, allocsz,
+                            /* CAML_DO_TRACK | */ CAML_FROM_CAML);
 }
 
 DECLARE_SIGNAL_HANDLER(handle_signal)
-- 
2.24.1