File: exception_comballoc.ml

package info (click to toggle)
ocaml 5.4.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 44,372 kB
  • sloc: ml: 370,196; ansic: 52,820; sh: 27,396; asm: 5,462; makefile: 3,679; python: 974; awk: 278; javascript: 273; perl: 59; fortran: 21; cs: 9
file content (158 lines) | stat: -rw-r--r-- 5,292 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
(* TEST *)

(* Tests that an exception in the alloc_minor callback, during a
   combined allocation, causes already-run allocation callbacks to
   be reflected by deallocation callbacks. *)

exception MyExc of string

module MP = Gc.Memprof

(* Similar infrastructure to stop_start_in_callback test *)

(* We need sets of 3-tuples of integers *)

module Int3Tuples =
struct
  type t = int * int * int
  let compare (x0,y0,z0) (x1,y1,z1) =
    match Stdlib.compare x0 x1 with
    | 0 -> (match Stdlib.compare y0 y1 with
            | 0 -> Stdlib.compare z0 z1
            | c -> c)
    | c -> c
end

module AllocSet = Set.Make(Int3Tuples)

(* A combined 7-block 33-word allocation *)

let[@inline never] f33 n =
  ((n, n, (n, n, n, (n,n,n,n,n))), (n, n, (n, n, n, (n,n,n,n,0))))

(* Raise exceptions from allocation callbacks.

   In the native code backend, we have combined allocations. If a
   single allocation callback from a combined allocation raises an
   exception, none of the blocks in that combined allocation are
   actually allocated. However, some allocation callbacks may have
   already been called, before the exception is raised, so statmemprof
   causes their deallocation callbacks also to be called, so that
   allocation and deallocation callbacks can be matched up.

   In the bytecode backend, there are no combined allocations, so
   these special cases don't apply: allocation callbacks called before
   the one which raises an exception reflect actual allocations which
   happened at that time, so statmemprof doesn't have to fake
   corresponding deallocations. *)

let raise_in_alloc () =
  let n_alloc = ref 0 in  (* number of allocations in current profile *)
  let n_prof = ref 0 in   (* number of profiles *)
  let n_exc = ref 0 in    (* number of exceptions handled *)
  let excs = ref AllocSet.empty in

  (* sets of (profile count, allocation count, size), for each operation *)
  let allocs = ref AllocSet.empty in
  let deallocs = ref AllocSet.empty in

  let record s (p, a, sz) = s := AllocSet.add (p,a,sz) (!s) in
  let dealloc_minor minor = (record deallocs minor; ()) in
  let dealloc_major major = (record deallocs major; ()) in

  let alloc_minor (info:MP.allocation) =
      (incr n_alloc;
       let p = !n_prof in
       let a = !n_alloc in
       let sz = info.size + 1 in (* add 1 for header word *)
       record allocs (p,a,sz);
       (* stop profile N after N allocations *)
       if a >= p then
       (record excs (p,a,sz);
        raise (MyExc "from allocation callback"));
       Some (p, a, sz)) in

  let promote minor = Some minor in
  let alloc_major info = (assert false) in   (* We don't expect any *)

  let tracker = { MP.alloc_minor ;
                  dealloc_minor ;
                  promote ;
                  alloc_major ;
                  dealloc_major } in

  let start () = (incr n_prof;
                  n_alloc := 0;
                  ignore (MP.start ~sampling_rate:1.0 tracker)) in

  let arr = ref [] in

  for i = 1 to 10 do
    start ();
    (try
      arr := (f33 42) :: (!arr);
    with
      MyExc s -> (incr n_exc));
    MP.stop();
    Gc.minor();
  done;
  arr := [];
  Gc.full_major();

  let alloc_size =
      AllocSet.fold (fun (p,a,sz) tot -> tot + sz) (!allocs) 0 in
  let alloc_count = AllocSet.cardinal (!allocs) in
  let dealloc_size =
      AllocSet.fold (fun (p,a,sz) tot -> tot + sz) (!deallocs) 0 in
  let dealloc_count = AllocSet.cardinal (!deallocs) in

  (* Every allocation callback is either raised or deallocated *)
  assert (AllocSet.disjoint (!deallocs) (!excs));
  assert (AllocSet.equal (AllocSet.union (!deallocs) (!excs)) (!allocs));

  (* Each call to f33 would allocates 7 blocks of 33 words,
     (sizes 6, 5, 4, 6, 5, 4, 3) plus the 3 words for the cons cell to
     add the result to !arr, making 8 blocks of 36 words.

     So we see this behaviour, as we iterate through the loop:
       i allocs exn words
       1      1   1     6   6
       2      2   1    11   6+5
       3      3   1    15   6+5+4
       4      4   1    21   6+5+4+6
       5      5   1    26   6+5+4+6+5
       6      6   1    30   6+5+4+6+5+4
       7      7   1    33   6+5+4+6+5+4+3
       8      8   1    36   6+5+4+6+5+4+3+3
       9      8   0    36   6+5+4+6+5+4+3+3
      10      8   0    36   6+5+4+6+5+4+3+3

             52   8   250   total

     and of those "allocations" (most of which never actually take
     place with the native code backend), the profile sees
     deallocations for all except 8 (the ones for which the callbacks
     raise exceptions), which add up to 36 words.

   *)

  assert (dealloc_count = 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 8);
  assert (alloc_count = dealloc_count + !n_exc);

  assert (dealloc_size = (0 +
                          6 +
                          6 + 5 +
                          6 + 5 + 4 +
                          6 + 5 + 4 + 6 +
                          6 + 5 + 4 + 6 + 5 +
                          6 + 5 + 4 + 6 + 5 + 4 +
                          6 + 5 + 4 + 6 + 5 + 4 + 3 +
                          6 + 5 + 4 + 6 + 5 + 4 + 3 + 3 +
                          6 + 5 + 4 + 6 + 5 + 4 + 3 + 3));

  assert (alloc_size = dealloc_size +
                       (6 + 5 + 4 + 6 + 5 + 4 + 3 + 3));
  arr


let _ = raise_in_alloc ()