summaryrefslogtreecommitdiffstats
path: root/Lib/trace.py
blob: 86b2101763bfa469aba2aaf13d2b816850a4464f (plain)
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
#!/usr/bin/env python3

# portions copyright 2001, Autonomous Zones Industries, Inc., all rights...
# err...  reserved and offered to the public under the terms of the
# Python 2.2 license.
# Author: Zooko O'Whielacronx
# http://zooko.com/
# mailto:zooko@zooko.com
#
# Copyright 2000, Mojam Media, Inc., all rights reserved.
# Author: Skip Montanaro
#
# Copyright 1999, Bioreason, Inc., all rights reserved.
# Author: Andrew Dalke
#
# Copyright 1995-1997, Automatrix, Inc., all rights reserved.
# Author: Skip Montanaro
#
# Copyright 1991-1995, Stichting Mathematisch Centrum, all rights reserved.
#
#
# Permission to use, copy, modify, and distribute this Python software and
# its associated documentation for any purpose without fee is hereby
# granted, provided that the above copyright notice appears in all copies,
# and that both that copyright notice and this permission notice appear in
# supporting documentation, and that the name of neither Automatrix,
# Bioreason or Mojam Media be used in advertising or publicity pertaining to
# distribution of the software without specific, written prior permission.
#
"""program/module to trace Python program or function execution

Sample use, command line:
  trace.py -c -f counts --ignore-dir '$prefix' spam.py eggs
  trace.py -t --ignore-dir '$prefix' spam.py eggs
  trace.py --trackcalls spam.py eggs

Sample use, programmatically
  import sys

  # create a Trace object, telling it what to ignore, and whether to
  # do tracing or line-counting or both.
  tracer = trace.Trace(ignoredirs=[sys.base_prefix, sys.base_exec_prefix,],
                       trace=0, count=1)
  # run the new command using the given tracer
  tracer.run('main()')
  # make a report, placing output in /tmp
  r = tracer.results()
  r.write_results(show_missing=True, coverdir="/tmp")
"""
__all__ = ['Trace', 'CoverageResults']

import linecache
import os
import re
import sys
import token
import tokenize
import inspect
import gc
import dis
import pickle
from time import monotonic as _time

import threading

PRAGMA_NOCOVER = "#pragma NO COVER"

class _Ignore:
    def __init__(self, modules=None, dirs=None):
        self._mods = set() if not modules else set(modules)
        self._dirs = [] if not dirs else [os.path.normpath(d)
                                          for d in dirs]
        self._ignore = { '<string>': 1 }

    def names(self, filename, modulename):
        if modulename in self._ignore:
            return self._ignore[modulename]

        # haven't seen this one before, so see if the module name is
        # on the ignore list.
        if modulename in self._mods:  # Identical names, so ignore
            self._ignore[modulename] = 1
            return 1

        # check if the module is a proper submodule of something on
        # the ignore list
        for mod in self._mods:
            # Need to take some care since ignoring
            # "cmp" mustn't mean ignoring "cmpcache" but ignoring
            # "Spam" must also mean ignoring "Spam.Eggs".
            if modulename.startswith(mod + '.'):
                self._ignore[modulename] = 1
                return 1

        # Now check that filename isn't in one of the directories
        if filename is None:
            # must be a built-in, so we must ignore
            self._ignore[modulename] = 1
            return 1

        # Ignore a file when it contains one of the ignorable paths
        for d in self._dirs:
            # The '+ os.sep' is to ensure that d is a parent directory,
            # as compared to cases like:
            #  d = "/usr/local"
            #  filename = "/usr/local.py"
            # or
            #  d = "/usr/local.py"
            #  filename = "/usr/local.py"
            if filename.startswith(d + os.sep):
                self._ignore[modulename] = 1
                return 1

        # Tried the different ways, so we don't ignore this module
        self._ignore[modulename] = 0
        return 0

def _modname(path):
    """Return a plausible module name for the patch."""

    base = os.path.basename(path)
    filename, ext = os.path.splitext(base)
    return filename

def _fullmodname(path):
    """Return a plausible module name for the path."""

    # If the file 'path' is part of a package, then the filename isn't
    # enough to uniquely identify it.  Try to do the right thing by
    # looking in sys.path for the longest matching prefix.  We'll
    # assume that the rest is the package name.

    comparepath = os.path.normcase(path)
    longest = ""
    for dir in sys.path:
        dir = os.path.normcase(dir)
        if comparepath.startswith(dir) and comparepath[len(dir)] == os.sep:
            if len(dir) > len(longest):
                longest = dir

    if longest:
        base = path[len(longest) + 1:]
    else:
        base = path
    # the drive letter is never part of the module name
    drive, base = os.path.splitdrive(base)
    base = base.replace(os.sep, ".")
    if os.altsep:
        base = base.replace(os.altsep, ".")
    filename, ext = os.path.splitext(base)
    return filename.lstrip(".")

class CoverageResults:
    def __init__(self, counts=None, calledfuncs=None, infile=None,
                 callers=None, outfile=None):
        self.counts = counts
        if self.counts is None:
            self.counts = {}
        self.counter = self.counts.copy() # map (filename, lineno) to count
        self.calledfuncs = calledfuncs
        if self.calledfuncs is None:
            self.calledfuncs = {}
        self.calledfuncs = self.calledfuncs.copy()
        self.callers = callers
        if self.callers is None:
            self.callers = {}
        self.callers = self.callers.copy()
        self.infile = infile
        self.outfile = outfile
        if self.infile:
            # Try to merge existing counts file.
            try:
                with open(self.infile, 'rb') as f:
                    counts, calledfuncs, callers = pickle.load(f)
                self.update(self.__class__(counts, calledfuncs, callers))
            except (OSError, EOFError, ValueError) as err:
                print(("Skipping counts file %r: %s"
                                      % (self.infile, err)), file=sys.stderr)

    def is_ignored_filename(self, filename):
        """Return True if the filename does not refer to a file
        we want to have reported.
        """
        return filename.startswith('<') and filename.endswith('>')

    def update(self, other):
        """Merge in the data from another CoverageResults"""
        counts = self.counts
        calledfuncs = self.calledfuncs
        callers = self.callers
        other_counts = other.counts
        other_calledfuncs = other.calledfuncs
        other_callers = other.callers

        for key in other_counts:
            counts[key] = counts.get(key, 0) + other_counts[key]

        for key in other_calledfuncs:
            calledfuncs[key] = 1

        for key in other_callers:
            callers[key] = 1

    def write_results(self, show_missing=True, summary=False, coverdir=None):
        """
        Write the coverage results.

        :param show_missing: Show lines that had no hits.
        :param summary: Include coverage summary per module.
        :param coverdir: If None, the results of each module are placed in its
                         directory, otherwise it is included in the directory
                         specified.
        """
        if self.calledfuncs:
            print()
            print("functions called:")
            calls = self.calledfuncs
            for filename, modulename, funcname in sorted(calls):
                print(("filename: %s, modulename: %s, funcname: %s"
                       % (filename, modulename, funcname)))

        if self.callers:
            print()
            print("calling relationships:")
            lastfile = lastcfile = ""
            for ((pfile, pmod, pfunc), (cfile, cmod, cfunc)) \
                    in sorted(self.callers):
                if pfile != lastfile:
                    print()
                    print("***", pfile, "***")
                    lastfile = pfile
                    lastcfile = ""
                if cfile != pfile and lastcfile != cfile:
                    print("  -->", cfile)
                    lastcfile = cfile
                print("    %s.%s -> %s.%s" % (pmod, pfunc, cmod, cfunc))

        # turn the counts data ("(filename, lineno) = count") into something
        # accessible on a per-file basis
        per_file = {}
        for filename, lineno in self.counts:
            lines_hit = per_file[filename] = per_file.get(filename, {})
            lines_hit[lineno] = self.counts[(filename, lineno)]

        # accumulate summary info, if needed
        sums = {}

        for filename, count in per_file.items():
            if self.is_ignored_filename(filename):
                continue

            if filename.endswith(".pyc"):
                filename = filename[:-1]

            if coverdir is None:
                dir = os.path.dirname(os.path.abspath(filename))
                modulename = _modname(filename)
            else:
                dir = coverdir
                if not os.path.exists(dir):
                    os.makedirs(dir)
                modulename = _fullmodname(filename)

            # If desired, get a list of the line numbers which represent
            # executable content (returned as a dict for better lookup speed)
            if show_missing:
                lnotab = _find_executable_linenos(filename)
            else:
                lnotab = {}
            source = linecache.getlines(filename)
            coverpath = os.path.join(dir, modulename + ".cover")
            with open(filename, 'rb') as fp:
                encoding, _ = tokenize.detect_encoding(fp.readline)
            n_hits, n_lines = self.write_results_file(coverpath, source,
                                                      lnotab, count, encoding)
            if summary and n_lines:
                percent = int(100 * n_hits / n_lines)
                sums[modulename] = n_lines, percent, modulename, filename


        if summary and sums:
            print("lines   cov%   module   (path)")
            for m in sorted(sums):
                n_lines, percent, modulename, filename = sums[m]
                print("%5d   %3d%%   %s   (%s)" % sums[m])

        if self.outfile:
            # try and store counts and module info into self.outfile
            try:
                pickle.dump((self.counts, self.calledfuncs, self.callers),
                            open(self.outfile, 'wb'), 1)
            except OSError as err:
                print("Can't save counts files because %s" % err, file=sys.stderr)

    def write_results_file(self, path, lines, lnotab, lines_hit, encoding=None):
        """Return a coverage results file in path."""
        # ``lnotab`` is a dict of executable lines, or a line number "table"

        try:
            outfile = open(path, "w", encoding=encoding)
        except OSError as err:
            print(("trace: Could not open %r for writing: %s"
                                  "- skipping" % (path, err)), file=sys.stderr)
            return 0, 0

        n_lines = 0
        n_hits = 0
        with outfile:
            for lineno, line in enumerate(lines, 1):
                # do the blank/comment match to try to mark more lines
                # (help the reader find stuff that hasn't been covered)
                if lineno in lines_hit:
                    outfile.write("%5d: " % lines_hit[lineno])
                    n_hits += 1
                    n_lines += 1
                elif lineno in lnotab and not PRAGMA_NOCOVER in line:
                    # Highlight never-executed lines, unless the line contains
                    # #pragma: NO COVER
                    outfile.write(">>>>>> ")
                    n_lines += 1
                else:
                    outfile.write("       ")
                outfile.write(line.expandtabs(8))

        return n_hits, n_lines

def _find_lines_from_code(code, strs):
    """Return dict where keys are lines in the line number table."""
    linenos = {}

    for _, lineno in dis.findlinestarts(code):
        if lineno not in strs:
            linenos[lineno] = 1

    return linenos

def _find_lines(code, strs):
    """Return lineno dict for all code objects reachable from code."""
    # get all of the lineno information from the code of this scope level
    linenos = _find_lines_from_code(code, strs)

    # and check the constants for references to other code objects
    for c in code.co_consts:
        if inspect.iscode(c):
            # find another code object, so recurse into it
            linenos.update(_find_lines(c, strs))
    return linenos

def _find_strings(filename, encoding=None):
    """Return a dict of possible docstring positions.

    The dict maps line numbers to strings.  There is an entry for
    line that contains only a string or a part of a triple-quoted
    string.
    """
    d = {}
    # If the first token is a string, then it's the module docstring.
    # Add this special case so that the test in the loop passes.
    prev_ttype = token.INDENT
    with open(filename, encoding=encoding) as f:
        tok = tokenize.generate_tokens(f.readline)
        for ttype, tstr, start, end, line in tok:
            if ttype == token.STRING:
                if prev_ttype == token.INDENT:
                    sline, scol = start
                    eline, ecol = end
                    for i in range(sline, eline + 1):
                        d[i] = 1
            prev_ttype = ttype
    return d

def _find_executable_linenos(filename):
    """Return dict where keys are line numbers in the line number table."""
    try:
        with tokenize.open(filename) as f:
            prog = f.read()
            encoding = f.encoding
    except OSError as err:
        print(("Not printing coverage data for %r: %s"
                              % (filename, err)), file=sys.stderr)
        return {}
    code = compile(prog, filename, "exec")
    strs = _find_strings(filename, encoding)
    return _find_lines(code, strs)

class Trace:
    def __init__(self, count=1, trace=1, countfuncs=0, countcallers=0,
                 ignoremods=(), ignoredirs=(), infile=None, outfile=None,
                 timing=False):
        """
        @param count true iff it should count number of times each
                     line is executed
        @param trace true iff it should print out each line that is
                     being counted
        @param countfuncs true iff it should just output a list of
                     (filename, modulename, funcname,) for functions
                     that were called at least once;  This overrides
                     `count' and `trace'
        @param ignoremods a list of the names of modules to ignore
        @param ignoredirs a list of the names of directories to ignore
                     all of the (recursive) contents of
        @param infile file from which to read stored counts to be
                     added into the results
        @param outfile file in which to write the results
        @param timing true iff timing information be displayed
        """
        self.infile = infile
        self.outfile = outfile
        self.ignore = _Ignore(ignoremods, ignoredirs)
        self.counts = {}   # keys are (filename, linenumber)
        self.pathtobasename = {} # for memoizing os.path.basename
        self.donothing = 0
        self.trace = trace
        self._calledfuncs = {}
        self._callers = {}
        self._caller_cache = {}
        self.start_time = None
        if timing:
            self.start_time = _time()
        if countcallers:
            self.globaltrace = self.globaltrace_trackcallers
        elif countfuncs:
            self.globaltrace = self.globaltrace_countfuncs
        elif trace and count:
            self.globaltrace = self.globaltrace_lt
            self.localtrace = self.localtrace_trace_and_count
        elif trace:
            self.globaltrace = self.globaltrace_lt
            self.localtrace = self.localtrace_trace
        elif count:
            self.globaltrace = self.globaltrace_lt
            self.localtrace = self.localtrace_count
        else:
            # Ahem -- do nothing?  Okay.
            self.donothing = 1

    def run(self, cmd):
        import __main__
        dict = __main__.__dict__
        self.runctx(cmd, dict, dict)

    def runctx(self, cmd, globals=None, locals=None):
        if globals is None: globals = {}
        if locals is None: locals = {}
        if not self.donothing:
            threading.settrace(self.globaltrace)
            sys.settrace(self.globaltrace)
        try:
            exec(cmd, globals, locals)
        finally:
            if not self.donothing:
                sys.settrace(None)
                threading.settrace(None)

    def runfunc(self, func, *args, **kw):
        result = None
        if not self.donothing:
            sys.settrace(self.globaltrace)
        try:
            result = func(*args, **kw)
        finally:
            if not self.donothing:
                sys.settrace(None)
        return result

    def file_module_function_of(self, frame):
        code = frame.f_code
        filename = code.co_filename
        if filename:
            modulename = _modname(filename)
        else:
            modulename = None

        funcname = code.co_name
        clsname = None
        if code in self._caller_cache:
            if self._caller_cache[code] is not None:
                clsname = self._caller_cache[code]
        else:
            self._caller_cache[code] = None
            ## use of gc.get_referrers() was suggested by Michael Hudson
            # all functions which refer to this code object
            funcs = [f for f in gc.get_referrers(code)
                         if inspect.isfunction(f)]
            # require len(func) == 1 to avoid ambiguity caused by calls to
            # new.function(): "In the face of ambiguity, refuse the
            # temptation to guess."
            if len(funcs) == 1:
                dicts = [d for d in gc.get_referrers(funcs[0])
                             if isinstance(d, dict)]
                if len(dicts) == 1:
                    classes = [c for c in gc.get_referrers(dicts[0])
                                   if hasattr(c, "__bases__")]
                    if len(classes) == 1:
                        # ditto for new.classobj()
                        clsname = classes[0].__name__
                        # cache the result - assumption is that new.* is
                        # not called later to disturb this relationship
                        # _caller_cache could be flushed if functions in
                        # the new module get called.
                        self._caller_cache[code] = clsname
        if clsname is not None:
            funcname = "%s.%s" % (clsname, funcname)

        return filename, modulename, funcname

    def globaltrace_trackcallers(self, frame, why, arg):
        """Handler for call events.

        Adds information about who called who to the self._callers dict.
        """
        if why == 'call':
            # XXX Should do a better job of identifying methods
            this_func = self.file_module_function_of(frame)
            parent_func = self.file_module_function_of(frame.f_back)
            self._callers[(parent_func, this_func)] = 1

    def globaltrace_countfuncs(self, frame, why, arg):
        """Handler for call events.

        Adds (filename, modulename, funcname) to the self._calledfuncs dict.
        """
        if why == 'call':
            this_func = self.file_module_function_of(frame)
            self._calledfuncs[this_func] = 1

    def globaltrace_lt(self, frame, why, arg):
        """Handler for call events.

        If the code block being entered is to be ignored, returns `None',
        else returns self.localtrace.
        """
        if why == 'call':
            code = frame.f_code
            filename = frame.f_globals.get('__file__', None)
            if filename:
                # XXX _modname() doesn't work right for packages, so
                # the ignore support won't work right for packages
                modulename = _modname(filename)
                if modulename is not None:
                    ignore_it = self.ignore.names(filename, modulename)
                    if not ignore_it:
                        if self.trace:
                            print((" --- modulename: %s, funcname: %s"
                                   % (modulename, code.co_name)))
                        return self.localtrace
            else:
                return None

    def localtrace_trace_and_count(self, frame, why, arg):
        if why == "line":
            # record the file name and line number of every trace
            filename = frame.f_code.co_filename
            lineno = frame.f_lineno
            key = filename, lineno
            self.counts[key] = self.counts.get(key, 0) + 1

            if self.start_time:
                print('%.2f' % (_time() - self.start_time), end=' ')
            bname = os.path.basename(filename)
            print("%s(%d): %s" % (bname, lineno,
                                  linecache.getline(filename, lineno)), end='')
        return self.localtrace

    def localtrace_trace(self, frame, why, arg):
        if why == "line":
            # record the file name and line number of every trace
            filename = frame.f_code.co_filename
            lineno = frame.f_lineno

            if self.start_time:
                print('%.2f' % (_time() - self.start_time), end=' ')
            bname = os.path.basename(filename)
            print("%s(%d): %s" % (bname, lineno,
                                  linecache.getline(filename, lineno)), end='')
        return self.localtrace

    def localtrace_count(self, frame, why, arg):
        if why == "line":
            filename = frame.f_code.co_filename
            lineno = frame.f_lineno
            key = filename, lineno
            self.counts[key] = self.counts.get(key, 0) + 1
        return self.localtrace

    def results(self):
        return CoverageResults(self.counts, infile=self.infile,
                               outfile=self.outfile,
                               calledfuncs=self._calledfuncs,
                               callers=self._callers)

def main():
    import argparse

    parser = argparse.ArgumentParser()
    parser.add_argument('--version', action='version', version='trace 2.0')

    grp = parser.add_argument_group('Main options',
            'One of these (or --report) must be given')

    grp.add_argument('-c', '--count', action='store_true',
            help='Count the number of times each line is executed and write '
                 'the counts to <module>.cover for each module executed, in '
                 'the module\'s directory. See also --coverdir, --file, '
                 '--no-report below.')
    grp.add_argument('-t', '--trace', action='store_true',
            help='Print each line to sys.stdout before it is executed')
    grp.add_argument('-l', '--listfuncs', action='store_true',
            help='Keep track of which functions are executed at least once '
                 'and write the results to sys.stdout after the program exits. '
                 'Cannot be specified alongside --trace or --count.')
    grp.add_argument('-T', '--trackcalls', action='store_true',
            help='Keep track of caller/called pairs and write the results to '
                 'sys.stdout after the program exits.')

    grp = parser.add_argument_group('Modifiers')

    _grp = grp.add_mutually_exclusive_group()
    _grp.add_argument('-r', '--report', action='store_true',
            help='Generate a report from a counts file; does not execute any '
                 'code. --file must specify the results file to read, which '
                 'must have been created in a previous run with --count '
                 '--file=FILE')
    _grp.add_argument('-R', '--no-report', action='store_true',
            help='Do not generate the coverage report files. '
                 'Useful if you want to accumulate over several runs.')

    grp.add_argument('-f', '--file',
            help='File to accumulate counts over several runs')
    grp.add_argument('-C', '--coverdir',
            help='Directory where the report files go. The coverage report '
                 'for <package>.<module> will be written to file '
                 '<dir>/<package>/<module>.cover')
    grp.add_argument('-m', '--missing', action='store_true',
            help='Annotate executable lines that were not executed with '
                 '">>>>>> "')
    grp.add_argument('-s', '--summary', action='store_true',
            help='Write a brief summary for each file to sys.stdout. '
                 'Can only be used with --count or --report')
    grp.add_argument('-g', '--timing', action='store_true',
            help='Prefix each line with the time since the program started. '
                 'Only used while tracing')

    grp = parser.add_argument_group('Filters',
            'Can be specified multiple times')
    grp.add_argument('--ignore-module', action='append', default=[],
            help='Ignore the given module(s) and its submodules'
                 '(if it is a package). Accepts comma separated list of '
                 'module names.')
    grp.add_argument('--ignore-dir', action='append', default=[],
            help='Ignore files in the given directory '
                 '(multiple directories can be joined by os.pathsep).')

    parser.add_argument('filename', nargs='?',
            help='file to run as main program')
    parser.add_argument('arguments', nargs=argparse.REMAINDER,
            help='arguments to the program')

    opts = parser.parse_args()

    if opts.ignore_dir:
        rel_path = 'lib', 'python{0.major}.{0.minor}'.format(sys.version_info)
        _prefix = os.path.join(sys.base_prefix, *rel_path)
        _exec_prefix = os.path.join(sys.base_exec_prefix, *rel_path)

    def parse_ignore_dir(s):
        s = os.path.expanduser(os.path.expandvars(s))
        s = s.replace('$prefix', _prefix).replace('$exec_prefix', _exec_prefix)
        return os.path.normpath(s)

    opts.ignore_module = [mod.strip()
                          for i in opts.ignore_module for mod in i.split(',')]
    opts.ignore_dir = [parse_ignore_dir(s)
                       for i in opts.ignore_dir for s in i.split(os.pathsep)]

    if opts.report:
        if not opts.file:
            parser.error('-r/--report requires -f/--file')
        results = CoverageResults(infile=opts.file, outfile=opts.file)
        return results.write_results(opts.missing, opts.summary, opts.coverdir)

    if not any([opts.trace, opts.count, opts.listfuncs, opts.trackcalls]):
        parser.error('must specify one of --trace, --count, --report, '
                     '--listfuncs, or --trackcalls')

    if opts.listfuncs and (opts.count or opts.trace):
        parser.error('cannot specify both --listfuncs and (--trace or --count)')

    if opts.summary and not opts.count:
        parser.error('--summary can only be used with --count or --report')

    if opts.filename is None:
        parser.error('filename is missing: required with the main options')

    sys.argv = [opts.filename, *opts.arguments]
    sys.path[0] = os.path.dirname(opts.filename)

    t = Trace(opts.count, opts.trace, countfuncs=opts.listfuncs,
              countcallers=opts.trackcalls, ignoremods=opts.ignore_module,
              ignoredirs=opts.ignore_dir, infile=opts.file,
              outfile=opts.file, timing=opts.timing)
    try:
        with open(opts.filename) as fp:
            code = compile(fp.read(), opts.filename, 'exec')
        # try to emulate __main__ namespace as much as possible
        globs = {
            '__file__': opts.filename,
            '__name__': '__main__',
            '__package__': None,
            '__cached__': None,
        }
        t.runctx(code, globs, globs)
    except OSError as err:
        sys.exit("Cannot run file %r because: %s" % (sys.argv[0], err))
    except SystemExit:
        pass

    results = t.results()

    if not opts.no_report:
        results.write_results(opts.missing, opts.summary, opts.coverdir)

if __name__=='__main__':
    main()
nd to at least V18 * --version 3: upgrade low_bound to at least V110 * * Upgrading low_bound will give the best format versions available for * that superblock version. Due to the possible upgrade, the fapl returned * from H5Fget_access_plist() might indicate a low_bound higher than what * the user originally set. * * After upgrading low_bound, the library will check to ensure that the * superblock version does not exceed the version allowed by high_bound. * Otherise fail file open. * * For details, please see RFC:Setting Bounds for Object Creation in HDF5 1.10.0. */ /* Check to ensure that superblock version 3 is used for SWMR-write access */ if (H5F_INTENT(f) & H5F_ACC_SWMR_WRITE) { if (sblock->super_vers < HDF5_SUPERBLOCK_VERSION_3) HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "superblock version for SWMR is less than 3") } /* Upgrade low_bound to at least V18 when encountering version 2 superblock */ if (sblock->super_vers == HDF5_SUPERBLOCK_VERSION_2) f->shared->low_bound = MAX(H5F_LIBVER_V18, f->shared->low_bound); /* Upgrade low_bound to at least V110 when encountering version 3 superblock */ if (sblock->super_vers >= HDF5_SUPERBLOCK_VERSION_3) f->shared->low_bound = MAX(H5F_LIBVER_V110, f->shared->low_bound); /* Version bounds check */ if (sblock->super_vers > HDF5_superblock_ver_bounds[f->shared->high_bound]) HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "superblock version exceeds high bound") /* Pin the superblock in the cache */ if (H5AC_pin_protected_entry(sblock) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTPIN, FAIL, "unable to pin superblock") /* Mark the superblock dirty if it was modified during loading */ if (((rw_flags & H5AC__READ_ONLY_FLAG) == 0) && udata.ignore_drvrinfo && udata.drvrinfo_removed) { HDassert(sblock->super_vers < HDF5_SUPERBLOCK_VERSION_2); sblock_flags |= H5AC__DIRTIED_FLAG; } /* end if */ /* The superblock must be flushed last (and collectively in parallel) */ sblock_flags |= H5AC__FLUSH_LAST_FLAG; #ifdef H5_HAVE_PARALLEL sblock_flags |= H5AC__FLUSH_COLLECTIVELY_FLAG; #endif /* H5_HAVE_PARALLEL */ /* Check if superblock address is different from base address and adjust * base address and "end of file" address if so. */ if (!H5F_addr_eq(super_addr, sblock->base_addr)) { /* Check if the superblock moved earlier in the file */ if (H5F_addr_lt(super_addr, sblock->base_addr)) udata.stored_eof -= (sblock->base_addr - super_addr); else /* The superblock moved later in the file */ udata.stored_eof += (super_addr - sblock->base_addr); /* Adjust base address for offsets of the HDF5 data in the file */ sblock->base_addr = super_addr; /* Set the base address for the file in the VFD now */ if (H5F__set_base_addr(f, sblock->base_addr) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "failed to set base address for file driver") /* Indicate that the superblock should be marked dirty */ if ((rw_flags & H5AC__READ_ONLY_FLAG) == 0) sblock_flags |= H5AC__DIRTIED_FLAG; } /* end if */ /* Set information in the file's creation property list */ if (H5P_set(c_plist, H5F_CRT_SUPER_VERS_NAME, &sblock->super_vers) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "unable to set superblock version") if (H5P_set(c_plist, H5F_CRT_ADDR_BYTE_NUM_NAME, &sblock->sizeof_addr) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "unable to set byte number in an address") if (H5P_set(c_plist, H5F_CRT_OBJ_BYTE_NUM_NAME, &sblock->sizeof_size) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "unable to set byte number for object size") /* Handle the B-tree 'K' values */ if (sblock->super_vers < HDF5_SUPERBLOCK_VERSION_2) { /* Sanity check */ HDassert(udata.sym_leaf_k != 0); /* Set the symbol table internal node 'K' value */ if (H5P_set(c_plist, H5F_CRT_SYM_LEAF_NAME, &udata.sym_leaf_k) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "unable to set rank for symbol table leaf nodes") sblock->sym_leaf_k = udata.sym_leaf_k; /* Set the B-tree internal node values, etc */ if (H5P_set(c_plist, H5F_CRT_BTREE_RANK_NAME, udata.btree_k) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "unable to set rank for btree internal nodes") H5MM_memcpy(sblock->btree_k, udata.btree_k, sizeof(unsigned) * (size_t)H5B_NUM_BTREE_ID); } /* end if */ else { /* Get the (default) B-tree internal node values, etc */ /* (Note: these may be reset in a superblock extension) */ if (H5P_get(c_plist, H5F_CRT_BTREE_RANK_NAME, sblock->btree_k) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTGET, FAIL, "unable to get rank for btree internal nodes") if (H5P_get(c_plist, H5F_CRT_SYM_LEAF_NAME, &sblock->sym_leaf_k) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTGET, FAIL, "unable to get rank for btree internal nodes") } /* end else */ /* * The user-defined data is the area of the file before the base * address. */ if (H5P_set(c_plist, H5F_CRT_USER_BLOCK_NAME, &sblock->base_addr) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "unable to set userblock size") /* * Make sure that the data is not truncated. One case where this is * possible is if the first file of a family of files was opened * individually. */ /* Can skip this test when it is not the initial file open-- * H5F__super_read() call from H5F_evict_tagged_metadata() for * refreshing object. * When flushing file buffers and fractal heap is involved, * the library will allocate actual space for tmp addresses * via the file layer. The aggregator allocates a block, * thus the eoa might be greater than eof. * Note: the aggregator is changed again after being reset * earlier before H5AC_flush due to allocation of tmp addresses. */ /* The EOF check must be skipped when the file is opened for SWMR read, * as the file can appear truncated if only part of it has been * been flushed to disk by the SWMR writer process. */ /* The EOF check is also skipped when the private property * H5F_ACS_SKIP_EOF_CHECK_NAME exists in the fapl. * This property is enabled by the tool h5clear with these * two options: (1) --filesize (2) --increment */ /* Check if this private property exists in fapl */ if (H5P_exist_plist(fa_plist, H5F_ACS_SKIP_EOF_CHECK_NAME) > 0) if (H5P_get(fa_plist, H5F_ACS_SKIP_EOF_CHECK_NAME, &skip_eof_check) < 0) HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get skip EOF check value") if (H5F_INTENT(f) & H5F_ACC_SWMR_READ) { /* * When the file is opened for SWMR read access, skip the check if: * --the file is already marked for SWMR writing and * --the file has version 3 superblock for SWMR support */ if ((sblock->status_flags & H5F_SUPER_SWMR_WRITE_ACCESS) && (sblock->status_flags & H5F_SUPER_WRITE_ACCESS) && sblock->super_vers >= HDF5_SUPERBLOCK_VERSION_3) skip_eof_check = TRUE; } /* end if */ if (!skip_eof_check && initial_read) { if (HADDR_UNDEF == (eof = H5FD_get_eof(f->shared->lf, H5FD_MEM_DEFAULT))) HGOTO_ERROR(H5E_FILE, H5E_CANTGET, FAIL, "unable to determine file size") /* (Account for the stored EOA being absolute offset -QAK) */ if ((eof + sblock->base_addr) < udata.stored_eof) HGOTO_ERROR(H5E_FILE, H5E_TRUNCATED, FAIL, "truncated file: eof = %llu, sblock->base_addr = %llu, stored_eof = %llu", (unsigned long long)eof, (unsigned long long)sblock->base_addr, (unsigned long long)udata.stored_eof) } /* end if */ /* * Tell the file driver how much address space has already been * allocated so that it knows how to allocate additional memory. */ /* Set the ring type in the API context */ H5AC_set_ring(H5AC_RING_SBE, NULL); /* Decode the optional driver information block */ if (H5F_addr_defined(sblock->driver_addr)) { H5O_drvinfo_t * drvinfo; /* Driver info */ H5F_drvrinfo_cache_ud_t drvrinfo_udata; /* User data for metadata callbacks */ unsigned drvinfo_flags = H5AC__NO_FLAGS_SET; /* Flags used in driver info block unprotect call */ /* Sanity check - driver info block should only be defined for * superblock version < 2. */ HDassert(sblock->super_vers < HDF5_SUPERBLOCK_VERSION_2); /* Set up user data */ drvrinfo_udata.f = f; drvrinfo_udata.driver_addr = sblock->driver_addr; /* extend EOA so we can read at least the fixed sized * portion of the driver info block */ if (H5FD_set_eoa(f->shared->lf, H5FD_MEM_SUPER, sblock->driver_addr + H5F_DRVINFOBLOCK_HDR_SIZE) < 0) /* will extend eoa later if required */ HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, FAIL, "set end of space allocation request failed") /* Look up the driver info block */ if (NULL == (drvinfo = (H5O_drvinfo_t *)H5AC_protect(f, H5AC_DRVRINFO, sblock->driver_addr, &drvrinfo_udata, rw_flags))) HGOTO_ERROR(H5E_FILE, H5E_CANTPROTECT, FAIL, "unable to load driver info block") /* Loading the driver info block is enough to set up the right info */ /* Check if we need to rewrite the driver info block info */ if (((rw_flags & H5AC__READ_ONLY_FLAG) == 0) && H5F_HAS_FEATURE(f, H5FD_FEAT_DIRTY_DRVRINFO_LOAD)) drvinfo_flags |= H5AC__DIRTIED_FLAG; /* set the pin entry flag so that the driver information block * cache entry will be pinned in the cache. */ drvinfo_flags |= H5AC__PIN_ENTRY_FLAG; /* Release the driver info block */ if (H5AC_unprotect(f, H5AC_DRVRINFO, sblock->driver_addr, drvinfo, drvinfo_flags) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTUNPROTECT, FAIL, "unable to release driver info block") /* save a pointer to the driver information cache entry */ f->shared->drvinfo = drvinfo; } /* end if */ /* (Account for the stored EOA being absolute offset -NAF) */ if (H5F__set_eoa(f, H5FD_MEM_DEFAULT, udata.stored_eof - sblock->base_addr) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "unable to set end-of-address marker for file") /* Decode the optional superblock extension info */ if (H5F_addr_defined(sblock->ext_addr)) { H5O_loc_t ext_loc; /* "Object location" for superblock extension */ H5O_btreek_t btreek; /* v1 B-tree 'K' value message from superblock extension */ H5O_drvinfo_t drvinfo; /* Driver info message from superblock extension */ size_t u; /* Local index variable */ htri_t status; /* Status for message existing */ /* Sanity check - superblock extension should only be defined for * superblock version >= 2. */ HDassert(sblock->super_vers >= HDF5_SUPERBLOCK_VERSION_2); /* Check for superblock extension being located "outside" the stored * 'eoa' value, which can occur with the split/multi VFD. */ if (H5F_addr_gt(sblock->ext_addr, udata.stored_eof)) { /* Set the 'eoa' for the object header memory type large enough * to give some room for a reasonably sized superblock extension. * (This is _rather_ a kludge -QAK) */ if (H5F__set_eoa(f, H5FD_MEM_OHDR, (haddr_t)(sblock->ext_addr + 1024)) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "unable to set end-of-address marker for file") } /* end if */ /* Open the superblock extension */ if (H5F__super_ext_open(f, sblock->ext_addr, &ext_loc) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTOPENOBJ, FAIL, "unable to open file's superblock extension") /* Check for the extension having a 'driver info' message */ if ((status = H5O_msg_exists(&ext_loc, H5O_DRVINFO_ID)) < 0) HGOTO_ERROR(H5E_FILE, H5E_EXISTS, FAIL, "unable to read object header") if (status) { /* Check for ignoring the driver info for this file */ if (!udata.ignore_drvrinfo) { /* Retrieve the 'driver info' structure */ if (NULL == H5O_msg_read(&ext_loc, H5O_DRVINFO_ID, &drvinfo)) HGOTO_ERROR(H5E_FILE, H5E_CANTGET, FAIL, "driver info message not present") /* Validate and decode driver information */ if (H5FD_sb_load(f->shared->lf, drvinfo.name, drvinfo.buf) < 0) { H5O_msg_reset(H5O_DRVINFO_ID, &drvinfo); HGOTO_ERROR(H5E_FILE, H5E_CANTDECODE, FAIL, "unable to decode driver information") } /* end if */ /* Reset driver info message */ H5O_msg_reset(H5O_DRVINFO_ID, &drvinfo); HDassert(FALSE == f->shared->drvinfo_sb_msg_exists); f->shared->drvinfo_sb_msg_exists = TRUE; } /* end else */ } /* end if */ /* Read in the shared OH message information if there is any */ if (H5SM_get_info(&ext_loc, c_plist) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTGET, FAIL, "unable to read SOHM table information") /* Check for the extension having a 'v1 B-tree "K"' message */ if ((status = H5O_msg_exists(&ext_loc, H5O_BTREEK_ID)) < 0) HGOTO_ERROR(H5E_FILE, H5E_EXISTS, FAIL, "unable to read object header") if (status) { /* Retrieve the 'v1 B-tree "K"' structure */ if (NULL == H5O_msg_read(&ext_loc, H5O_BTREEK_ID, &btreek)) HGOTO_ERROR(H5E_FILE, H5E_CANTGET, FAIL, "v1 B-tree 'K' info message not present") /* Set non-default v1 B-tree 'K' value info from file */ sblock->btree_k[H5B_CHUNK_ID] = btreek.btree_k[H5B_CHUNK_ID]; sblock->btree_k[H5B_SNODE_ID] = btreek.btree_k[H5B_SNODE_ID]; sblock->sym_leaf_k = btreek.sym_leaf_k; /* Set non-default v1 B-tree 'K' values in the property list */ if (H5P_set(c_plist, H5F_CRT_BTREE_RANK_NAME, btreek.btree_k) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "unable to set rank for btree internal nodes") if (H5P_set(c_plist, H5F_CRT_SYM_LEAF_NAME, &btreek.sym_leaf_k) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "unable to set rank for symbol table leaf nodes") } /* end if */ /* Check for the extension having a 'free-space manager info' message */ if ((status = H5O_msg_exists(&ext_loc, H5O_FSINFO_ID)) < 0) HGOTO_ERROR(H5E_FILE, H5E_EXISTS, FAIL, "unable to read object header") if (status) { uint8_t flags; /* Message flags */ /* Get message flags */ if (H5O_msg_get_flags(&ext_loc, H5O_FSINFO_ID, &flags) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTGET, FAIL, "unable to message flags for free-space manager info message") /* If message is NOT marked "unknown"--set up file space info */ if (!(flags & H5O_MSG_FLAG_WAS_UNKNOWN)) { H5O_fsinfo_t fsinfo; /* File space info message from superblock extension */ hbool_t null_fsm_addr = FALSE; /* Whether to drop free-space to the floor */ /* The h5clear tool uses this property to tell the library * to drop free-space to the floor */ if (H5P_exist_plist(fa_plist, H5F_ACS_NULL_FSM_ADDR_NAME) > 0) if (H5P_get(fa_plist, H5F_ACS_NULL_FSM_ADDR_NAME, &null_fsm_addr) < 0) HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get clearance for persisting fsm addr") /* Retrieve the 'file space info' structure */ if (NULL == H5O_msg_read(&ext_loc, H5O_FSINFO_ID, &fsinfo)) HGOTO_ERROR(H5E_FILE, H5E_CANTGET, FAIL, "unable to get free-space manager info message") /* Version bounds check */ if (H5O_fsinfo_ver_bounds[H5F_HIGH_BOUND(f)] == H5O_INVALID_VERSION || fsinfo.version > H5O_fsinfo_ver_bounds[H5F_HIGH_BOUND(f)]) HGOTO_ERROR(H5E_FILE, H5E_BADRANGE, FAIL, "File space info message's version out of bounds") /* Update changed values */ if (f->shared->fs_version != fsinfo.version) f->shared->fs_version = fsinfo.version; if (f->shared->fs_strategy != fsinfo.strategy) { f->shared->fs_strategy = fsinfo.strategy; /* Set non-default strategy in the property list */ if (H5P_set(c_plist, H5F_CRT_FILE_SPACE_STRATEGY_NAME, &fsinfo.strategy) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "unable to set file space strategy") } /* end if */ if (f->shared->fs_persist != fsinfo.persist) { f->shared->fs_persist = fsinfo.persist; /* Set non-default strategy in the property list */ if (H5P_set(c_plist, H5F_CRT_FREE_SPACE_PERSIST_NAME, &fsinfo.persist) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "unable to set file space strategy") } /* end if */ if (f->shared->fs_threshold != fsinfo.threshold) { f->shared->fs_threshold = fsinfo.threshold; /* Set non-default threshold in the property list */ if (H5P_set(c_plist, H5F_CRT_FREE_SPACE_THRESHOLD_NAME, &fsinfo.threshold) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "unable to set file space strategy") } /* end if */ HDassert(f->shared->fs_page_size >= H5F_FILE_SPACE_PAGE_SIZE_MIN); HDassert(fsinfo.page_size >= H5F_FILE_SPACE_PAGE_SIZE_MIN); if (f->shared->fs_page_size != fsinfo.page_size) { f->shared->fs_page_size = fsinfo.page_size; /* Set file space page size in the property list */ if (H5P_set(c_plist, H5F_CRT_FILE_SPACE_PAGE_SIZE_NAME, &fsinfo.page_size) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "unable to set file space page size") } /* end if */ if (f->shared->pgend_meta_thres != fsinfo.pgend_meta_thres) /* Initialize page end meta threshold */ f->shared->pgend_meta_thres = fsinfo.pgend_meta_thres; if (f->shared->eoa_pre_fsm_fsalloc != fsinfo.eoa_pre_fsm_fsalloc) f->shared->eoa_pre_fsm_fsalloc = fsinfo.eoa_pre_fsm_fsalloc; /* f->shared->eoa_pre_fsm_fsalloc must always be HADDR_UNDEF * in the absence of persistent free space managers. */ /* If the following two conditions are true: * (1) skipping EOF check (skip_eof_check) * (2) dropping free-space to the floor (null_fsm_addr) * skip the asserts as "eoa_pre_fsm_fsalloc" may be undefined * for a crashed file with persistent free space managers. * #1 and #2 are enabled when the tool h5clear --increment * option is used. */ if (!skip_eof_check && !null_fsm_addr) { HDassert((!f->shared->fs_persist) || (f->shared->eoa_pre_fsm_fsalloc != HADDR_UNDEF)); HDassert(!f->shared->first_alloc_dealloc); } /* end if */ /* As "eoa_pre_fsm_fsalloc" may be undefined for a crashed file * with persistent free space managers, therefore, set * "first_alloc_dealloc" when the condition * "dropping free-space to the floor is true. * This will ensure that no action is done to settle things on file * close via H5MF_settle_meta_data_fsm() and H5MF_settle_raw_data_fsm(). */ if ((f->shared->eoa_pre_fsm_fsalloc != HADDR_UNDEF || null_fsm_addr) && (H5F_INTENT(f) & H5F_ACC_RDWR)) f->shared->first_alloc_dealloc = TRUE; f->shared->fs_addr[0] = HADDR_UNDEF; for (u = 1; u < NELMTS(f->shared->fs_addr); u++) f->shared->fs_addr[u] = fsinfo.fs_addr[u - 1]; /* If the following two conditions are true: * (1) file is persisting free-space * (2) dropping free-space to the floor (null_fsm_addr) * nullify the addresses of the FSMs */ if (f->shared->fs_persist && null_fsm_addr) for (u = 0; u < NELMTS(fsinfo.fs_addr); u++) f->shared->fs_addr[u] = fsinfo.fs_addr[u] = HADDR_UNDEF; /* For fsinfo.mapped: remove the FSINFO message from the superblock extension and write a new message to the extension */ /* For null_fsm_addr: just update FSINFO message in the superblock extension */ if (((fsinfo.mapped || null_fsm_addr) && (rw_flags & H5AC__READ_ONLY_FLAG) == 0)) { /* Do the same kluge until we know for sure. VC */ #if 1 /* bug fix test code -- tidy this up if all goes well */ /* JRM */ /* KLUGE ALERT!! * * H5F__super_ext_write_msg() expects f->shared->sblock to * be set -- verify that it is NULL, and then set it. * Set it back to NULL when we are done. */ HDassert(f->shared->sblock == NULL); f->shared->sblock = sblock; #endif /* JRM */ if (null_fsm_addr) { if (H5F__super_ext_write_msg(f, H5O_FSINFO_ID, &fsinfo, FALSE, H5O_MSG_FLAG_MARK_IF_UNKNOWN) < 0) HGOTO_ERROR(H5E_FILE, H5E_WRITEERROR, FAIL, "error in writing fsinfo message to superblock extension") } else { if (H5F__super_ext_remove_msg(f, H5O_FSINFO_ID) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTDELETE, FAIL, "error in removing message from superblock extension") if (H5F__super_ext_write_msg(f, H5O_FSINFO_ID, &fsinfo, TRUE, H5O_MSG_FLAG_MARK_IF_UNKNOWN) < 0) HGOTO_ERROR(H5E_FILE, H5E_WRITEERROR, FAIL, "error in writing fsinfo message to superblock extension") } #if 1 /* bug fix test code -- tidy this up if all goes well */ /* JRM */ f->shared->sblock = NULL; #endif /* JRM */ } /* end if */ } /* end if not marked "unknown" */ } /* end if */ /* Check for the extension having a 'metadata cache image' message */ if ((status = H5O_msg_exists(&ext_loc, H5O_MDCI_MSG_ID)) < 0) HGOTO_ERROR(H5E_FILE, H5E_EXISTS, FAIL, "unable to read object header") if (status) { hbool_t rw = ((rw_flags & H5AC__READ_ONLY_FLAG) == 0); H5O_mdci_t mdci_msg; /* if the metadata cache image superblock extension message exists, * read its contents and pass the data on to the metadata cache. * Given this data, the cache will load and decode the metadata * cache image block, decoded it and load its contents into the * the cache on the test protect call. * * Further, if the file is opened R/W, the metadata cache will * delete the metadata cache image superblock extension and free * the cache image block. Don't do this now as f->shared * is not fully setup, which complicates matters. */ /* Retrieve the 'metadata cache image message' structure */ if (NULL == H5O_msg_read(&ext_loc, H5O_MDCI_MSG_ID, &mdci_msg)) HGOTO_ERROR(H5E_FILE, H5E_CANTGET, FAIL, "unable to get metadata cache image message") /* Indicate to the cache that there's an image to load on first protect call */ if (H5AC_load_cache_image_on_next_protect(f, mdci_msg.addr, mdci_msg.size, rw) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTLOAD, FAIL, "call to H5AC_load_cache_image_on_next_protect failed"); } /* end if */ /* Close superblock extension */ if (H5F__super_ext_close(f, &ext_loc, FALSE) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTCLOSEOBJ, FAIL, "unable to close file's superblock extension") } /* end if */ /* Update the driver info if VFD indicated to do so */ /* (NOTE: only for later versions of superblock, earlier versions are handled * earlier in this routine. */ if (((rw_flags & H5AC__READ_ONLY_FLAG) == 0) && sblock->super_vers >= HDF5_SUPERBLOCK_VERSION_2 && H5F_addr_defined(sblock->ext_addr)) { /* Check for modifying the driver info when opening the file */ if (H5F_HAS_FEATURE(f, H5FD_FEAT_DIRTY_DRVRINFO_LOAD)) { size_t driver_size; /* Size of driver info block (bytes) */ /* Check for driver info message */ H5_CHECKED_ASSIGN(driver_size, size_t, H5FD_sb_size(f->shared->lf), hsize_t); if (driver_size > 0) { H5O_drvinfo_t drvinfo; /* Driver info */ uint8_t dbuf[H5F_MAX_DRVINFOBLOCK_SIZE]; /* Driver info block encoding buffer */ /* Sanity check */ HDassert(driver_size <= H5F_MAX_DRVINFOBLOCK_SIZE); /* Encode driver-specific data */ if (H5FD_sb_encode(f->shared->lf, drvinfo.name, dbuf) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, FAIL, "unable to encode driver information") /* Set the driver info information for the superblock extension */ drvinfo.len = driver_size; drvinfo.buf = dbuf; /* Write driver info information to the superblock extension */ #if 1 /* bug fix test code -- tidy this up if all goes well */ /* JRM */ /* KLUGE ALERT!! * * H5F__super_ext_write_msg() expects f->shared->sblock to * be set -- verify that it is NULL, and then set it. * Set it back to NULL when we are done. */ HDassert(f->shared->sblock == NULL); f->shared->sblock = sblock; #endif /* JRM */ if (H5F__super_ext_write_msg(f, H5O_DRVINFO_ID, &drvinfo, FALSE, H5O_MSG_NO_FLAGS_SET) < 0) HGOTO_ERROR(H5E_FILE, H5E_WRITEERROR, FAIL, "error in writing message to superblock extension") #if 1 /* bug fix test code -- tidy this up if all goes well */ /* JRM */ f->shared->sblock = NULL; #endif /* JRM */ } /* end if */ } /* end if */ /* Check for eliminating the driver info block */ else if (H5F_HAS_FEATURE(f, H5FD_FEAT_IGNORE_DRVRINFO)) { /* Remove the driver info message from the superblock extension */ if (H5F__super_ext_remove_msg(f, H5O_DRVINFO_ID) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTRELEASE, FAIL, "error in removing message from superblock extension") /* Check if the superblock extension was removed */ if (!H5F_addr_defined(sblock->ext_addr)) sblock_flags |= H5AC__DIRTIED_FLAG; } /* end if */ } /* end if */ /* Set the pointer to the pinned superblock */ f->shared->sblock = sblock; /* Set the page aggregation mode */ if (H5F__set_paged_aggr(f, (hbool_t)H5F_PAGED_AGGR(f)) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "failed to set paged_aggr status for file driver") done: /* Reset the ring in the API context */ if (orig_ring != H5AC_RING_INV) H5AC_set_ring(orig_ring, NULL); /* Release the superblock */ if (sblock && H5AC_unprotect(f, H5AC_SUPERBLOCK, (haddr_t)0, sblock, sblock_flags) < 0) HDONE_ERROR(H5E_FILE, H5E_CANTUNPROTECT, FAIL, "unable to close superblock") /* If we have failed, make sure no entries are left in the * metadata cache, so that it can be shut down and discarded. */ if (ret_value < 0) { /* Unpin and discard drvinfo cache entry */ if (f->shared->drvinfo) { if (H5AC_unpin_entry(f->shared->drvinfo) < 0) HDONE_ERROR(H5E_FILE, H5E_CANTUNPIN, FAIL, "unable to unpin driver info") /* Evict the driver info block from the cache */ if (H5AC_expunge_entry(f, H5AC_DRVRINFO, sblock->driver_addr, H5AC__NO_FLAGS_SET) < 0) HDONE_ERROR(H5E_FILE, H5E_CANTEXPUNGE, FAIL, "unable to expunge driver info block") } /* end if */ /* Unpin & discard superblock */ if (sblock) { /* Unpin superblock in cache */ if (H5AC_unpin_entry(sblock) < 0) HDONE_ERROR(H5E_FILE, H5E_CANTUNPIN, FAIL, "unable to unpin superblock") /* Evict the superblock from the cache */ if (H5AC_expunge_entry(f, H5AC_SUPERBLOCK, (haddr_t)0, H5AC__NO_FLAGS_SET) < 0) HDONE_ERROR(H5E_FILE, H5E_CANTEXPUNGE, FAIL, "unable to expunge superblock") } /* end if */ } /* end if */ FUNC_LEAVE_NOAPI_TAG(ret_value) } /* end H5F__super_read() */ /*------------------------------------------------------------------------- * Function: H5F__super_init * * Purpose: Allocates the superblock for the file and initializes * information about the superblock in memory. Writes extension * messages if any are needed. * * Return: Success: SUCCEED * Failure: FAIL * * Programmer: Quincey Koziol * Sept 15, 2003 * *------------------------------------------------------------------------- */ herr_t H5F__super_init(H5F_t *f) { H5F_super_t *sblock = NULL; /* Superblock cache structure */ hbool_t sblock_in_cache = FALSE; /* Whether the superblock has been inserted into the metadata cache */ H5O_drvinfo_t *drvinfo = NULL; /* Driver info */ hbool_t drvinfo_in_cache = FALSE; /* Whether the driver info block has been inserted into the metadata cache */ H5P_genplist_t *plist; /* File creation property list */ H5AC_ring_t orig_ring = H5AC_RING_INV; hsize_t userblock_size; /* Size of userblock, in bytes */ hsize_t superblock_size; /* Size of superblock, in bytes */ size_t driver_size; /* Size of driver info block (bytes) */ unsigned super_vers = HDF5_SUPERBLOCK_VERSION_DEF; /* Superblock version for file */ H5O_loc_t ext_loc; /* Superblock extension object location */ hbool_t need_ext; /* Whether the superblock extension is needed */ hbool_t ext_created = FALSE; /* Whether the extension has been created */ hbool_t non_default_fs_settings = FALSE; /* Whether the file has non-default free-space settings */ herr_t ret_value = SUCCEED; /* Return Value */ FUNC_ENTER_PACKAGE_TAG(H5AC__SUPERBLOCK_TAG) /* Allocate space for the superblock */ if (NULL == (sblock = H5FL_CALLOC(H5F_super_t))) HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed") /* Initialize various address information */ sblock->base_addr = HADDR_UNDEF; sblock->ext_addr = HADDR_UNDEF; sblock->driver_addr = HADDR_UNDEF; sblock->root_addr = HADDR_UNDEF; /* Get the shared file creation property list */ if (NULL == (plist = (H5P_genplist_t *)H5I_object(f->shared->fcpl_id))) HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a property list") /* Initialize sym_leaf_k */ if (H5P_get(plist, H5F_CRT_SYM_LEAF_NAME, &sblock->sym_leaf_k) < 0) HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get byte number for object size") /* Initialize btree_k */ if (H5P_get(plist, H5F_CRT_BTREE_RANK_NAME, &sblock->btree_k[0]) < 0) HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "unable to get rank for btree internal nodes") /* Check for non-default free-space settings */ if (!(f->shared->fs_strategy == H5F_FILE_SPACE_STRATEGY_DEF && f->shared->fs_persist == H5F_FREE_SPACE_PERSIST_DEF && f->shared->fs_threshold == H5F_FREE_SPACE_THRESHOLD_DEF && f->shared->fs_page_size == H5F_FILE_SPACE_PAGE_SIZE_DEF)) non_default_fs_settings = TRUE; /* * When creating a file with write access, the library will: * -- set superblock version to 0, 1 or 2 based on feature enabled * -- no change to low_bound * When creating a file with SWMR-write access, the library will: * (See explanation (#) below.) * -- set superblock version to 3 * -- upgrade low_bound to at least V110 * * Then the library will finalize superblock version to that allowed by * low_bound if that is higher. * Lastly, the library will check to ensure the superblock version does not * exceed the version allowed by high_bound. Otherwise fail file open. * * For details, please see RFC:Setting Bounds for Object Creation in HDF5 1.10.0. * * (#) * Version 3 superblock is introduced in 1.10 for SWMR due to the problem of * the status_flags field in the superblock. The problem is discussed in * jira issue SWMR-79 and also in the RFC: File Format Changes in HDF5 1.10.0. * The file's low_bound is upgraded for SWMR so that the library will * use the best format versions available in 1.10. * Due to the possible upgrade, the fapl returned from H5Fget_access_plist() * might indicate a low_bound higher than what the user originally set. */ /* * Creating a file with SWMR-write access will * upgrade superblock version and low_bound */ if (H5F_INTENT(f) & H5F_ACC_SWMR_WRITE) { super_vers = HDF5_SUPERBLOCK_VERSION_3; f->shared->low_bound = MAX(H5F_LIBVER_V110, f->shared->low_bound); /* Bump superblock version to create superblock extension for SOHM info */ } else if (f->shared->sohm_nindexes > 0) super_vers = HDF5_SUPERBLOCK_VERSION_2; /* * Bump superblock version to create superblock extension for: * -- non-default file space strategy or * -- non-default persisting free-space or * -- non-default free-space threshold or * -- non-default page_size */ else if (non_default_fs_settings) super_vers = HDF5_SUPERBLOCK_VERSION_2; /* Check for non-default indexed storage B-tree internal 'K' value * and set the version # of the superblock to 1 if it is a non-default * value. */ else if (sblock->btree_k[H5B_CHUNK_ID] != HDF5_BTREE_CHUNK_IK_DEF) super_vers = HDF5_SUPERBLOCK_VERSION_1; /* Finalize superblock version to that allowed by the file's low bound if higher */ super_vers = MAX(super_vers, HDF5_superblock_ver_bounds[f->shared->low_bound]); /* Version bounds check */ if (super_vers > HDF5_superblock_ver_bounds[f->shared->high_bound]) HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "superblock version out of bounds") /* If a newer superblock version is required, set it here */ if (super_vers != HDF5_SUPERBLOCK_VERSION_DEF) { H5P_genplist_t *c_plist; /* Property list */ if (NULL == (c_plist = (H5P_genplist_t *)H5I_object(f->shared->fcpl_id))) HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not property list") if (H5P_set(c_plist, H5F_CRT_SUPER_VERS_NAME, &super_vers) < 0) HGOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "unable to set superblock version") } /* end if */ if (H5FD_set_paged_aggr(f->shared->lf, (hbool_t)H5F_PAGED_AGGR(f)) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, FAIL, "failed to set paged_aggr status for file driver") /* * The superblock starts immediately after the user-defined * header, which we have already insured is a proper size. The * base address is set to the same thing as the superblock for * now. */ if (H5P_get(plist, H5F_CRT_USER_BLOCK_NAME, &userblock_size) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTGET, FAIL, "unable to get userblock size") /* Sanity check the userblock size vs. the file's allocation alignment */ if (userblock_size > 0) { /* Set up the alignment to use for page or aggr fs */ hsize_t alignment = H5F_PAGED_AGGR(f) ? f->shared->fs_page_size : f->shared->alignment; if (userblock_size < alignment) HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "userblock size must be > file object alignment") if (0 != (userblock_size % alignment)) HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "userblock size must be an integral multiple of file object alignment") } /* end if */ sblock->base_addr = userblock_size; sblock->status_flags = 0; /* Reserve space for the userblock */ if (H5F__set_eoa(f, H5FD_MEM_SUPER, userblock_size) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, FAIL, "unable to set EOA value for userblock") /* Set the base address for the file in the VFD now, after allocating * space for userblock. */ if (H5F__set_base_addr(f, sblock->base_addr) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, FAIL, "failed to set base address for file driver") /* Save a local copy of the superblock version number, size of addresses & offsets */ sblock->super_vers = super_vers; sblock->sizeof_addr = f->shared->sizeof_addr; sblock->sizeof_size = f->shared->sizeof_size; /* Compute the size of the superblock */ superblock_size = (hsize_t)H5F_SUPERBLOCK_SIZE(sblock); /* Compute the size of the driver information block */ H5_CHECKED_ASSIGN(driver_size, size_t, H5FD_sb_size(f->shared->lf), hsize_t); /* The following code sets driver_size to the valued needed * for the driver info block, and sets the driver info block * address regardless of the version of the superblock. */ if (driver_size > 0) { /* Add in the driver info header, for older superblocks */ /* Superblock versions >= 2 will put the driver info in a message * and don't need the header -QAK, 1/4/2017 */ if (super_vers < HDF5_SUPERBLOCK_VERSION_2) driver_size += H5F_DRVINFOBLOCK_HDR_SIZE; /* * The file driver information block begins immediately after the * superblock. (relative to base address in file) */ sblock->driver_addr = superblock_size; } /* end if */ /* * Allocate space for the superblock & driver info block. * We do it with one allocation request because the superblock needs to be * at the beginning of the file and only the first allocation request is * required to return memory at format address zero. */ if (super_vers < HDF5_SUPERBLOCK_VERSION_2) superblock_size += driver_size; /* Set the ring type in the API context */ H5AC_set_ring(H5AC_RING_SB, &orig_ring); /* Insert superblock into cache, pinned */ if (H5AC_insert_entry(f, H5AC_SUPERBLOCK, (haddr_t)0, sblock, H5AC__PIN_ENTRY_FLAG | H5AC__FLUSH_LAST_FLAG | H5AC__FLUSH_COLLECTIVELY_FLAG) < 0) HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, "can't add superblock to cache") sblock_in_cache = TRUE; /* Keep a copy of the superblock info */ f->shared->sblock = sblock; /* Allocate space for the superblock */ if (HADDR_UNDEF == H5MF_alloc(f, H5FD_MEM_SUPER, superblock_size)) HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "file allocation failed for superblock") /* set the drvinfo filed to NULL -- will overwrite this later if needed */ f->shared->drvinfo = NULL; /* * Determine if we will need a superblock extension */ /* Files with SOHM indices always need the superblock extension */ if (f->shared->sohm_nindexes > 0) { HDassert(super_vers >= HDF5_SUPERBLOCK_VERSION_2); need_ext = TRUE; } /* end if */ /* Files with non-default free space settings always need the superblock extension */ else if (non_default_fs_settings) { HDassert(super_vers >= HDF5_SUPERBLOCK_VERSION_2); need_ext = TRUE; } /* end if */ /* If we're going to use a version of the superblock format which allows * for the superblock extension, check for non-default values to store * in it. */ else if (super_vers >= HDF5_SUPERBLOCK_VERSION_2) { /* Check for non-default v1 B-tree 'K' values to store */ if (sblock->btree_k[H5B_SNODE_ID] != HDF5_BTREE_SNODE_IK_DEF || sblock->btree_k[H5B_CHUNK_ID] != HDF5_BTREE_CHUNK_IK_DEF || sblock->sym_leaf_k != H5F_CRT_SYM_LEAF_DEF) need_ext = TRUE; /* Check for driver info to store */ else if (driver_size > 0) need_ext = TRUE; else need_ext = FALSE; } /* end if */ else need_ext = FALSE; /* Set the ring type in the API context */ H5AC_set_ring(H5AC_RING_SBE, NULL); /* Create the superblock extension for "extra" superblock data, if necessary. */ if (need_ext) { /* The superblock extension isn't actually a group, but the * default group creation list should work fine. * If we don't supply a size for the object header, HDF5 will * allocate H5O_MIN_SIZE by default. This is currently * big enough to hold the biggest possible extension, but should * be tuned if more information is added to the superblock * extension. */ if (H5F__super_ext_create(f, &ext_loc) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTCREATE, FAIL, "unable to create superblock extension") ext_created = TRUE; /* Create the Shared Object Header Message table and register it with * the metadata cache, if this file supports shared messages. */ if (f->shared->sohm_nindexes > 0) { /* Initialize the shared message code & write the SOHM message to the extension */ if (H5SM_init(f, plist, &ext_loc) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, FAIL, "unable to create SOHM table") } /* Check for non-default v1 B-tree 'K' values to store */ if (sblock->btree_k[H5B_SNODE_ID] != HDF5_BTREE_SNODE_IK_DEF || sblock->btree_k[H5B_CHUNK_ID] != HDF5_BTREE_CHUNK_IK_DEF || sblock->sym_leaf_k != H5F_CRT_SYM_LEAF_DEF) { H5O_btreek_t btreek; /* v1 B-tree 'K' value message for superblock extension */ /* Write v1 B-tree 'K' value information to the superblock extension */ btreek.btree_k[H5B_CHUNK_ID] = sblock->btree_k[H5B_CHUNK_ID]; btreek.btree_k[H5B_SNODE_ID] = sblock->btree_k[H5B_SNODE_ID]; btreek.sym_leaf_k = sblock->sym_leaf_k; if (H5O_msg_create(&ext_loc, H5O_BTREEK_ID, H5O_MSG_FLAG_CONSTANT | H5O_MSG_FLAG_DONTSHARE, H5O_UPDATE_TIME, &btreek) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, FAIL, "unable to update v1 B-tree 'K' value header message") } /* end if */ /* Check for driver info to store */ if (driver_size > 0) { H5O_drvinfo_t info; /* Driver info */ uint8_t dbuf[H5F_MAX_DRVINFOBLOCK_SIZE]; /* Driver info block encoding buffer */ /* Sanity check */ HDassert(driver_size <= H5F_MAX_DRVINFOBLOCK_SIZE); /* Encode driver-specific data */ HDmemset(dbuf, 0, sizeof(dbuf)); if (H5FD_sb_encode(f->shared->lf, info.name, dbuf) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, FAIL, "unable to encode driver information") /* Write driver info information to the superblock extension */ info.len = driver_size; info.buf = dbuf; if (H5O_msg_create(&ext_loc, H5O_DRVINFO_ID, H5O_MSG_FLAG_DONTSHARE, H5O_UPDATE_TIME, &info) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, FAIL, "unable to update driver info header message") HDassert(FALSE == f->shared->drvinfo_sb_msg_exists); f->shared->drvinfo_sb_msg_exists = TRUE; } /* end if */ /* Check for non-default free-space info settings */ if (non_default_fs_settings) { H5F_mem_page_t ptype; H5O_fsinfo_t fsinfo; /* Free space manager info message */ /* Write free-space manager info message to superblock extension object header if needed */ fsinfo.strategy = f->shared->fs_strategy; fsinfo.persist = f->shared->fs_persist; fsinfo.threshold = f->shared->fs_threshold; fsinfo.page_size = f->shared->fs_page_size; fsinfo.pgend_meta_thres = f->shared->pgend_meta_thres; fsinfo.eoa_pre_fsm_fsalloc = HADDR_UNDEF; fsinfo.mapped = FALSE; /* Set the version for the fsinfo message */ if (H5O__fsinfo_set_version(f, &fsinfo) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "can't set version of fsinfo") f->shared->fs_version = fsinfo.version; for (ptype = H5F_MEM_PAGE_SUPER; ptype < H5F_MEM_PAGE_NTYPES; ptype++) fsinfo.fs_addr[ptype - 1] = HADDR_UNDEF; if (H5O_msg_create(&ext_loc, H5O_FSINFO_ID, H5O_MSG_FLAG_DONTSHARE | H5O_MSG_FLAG_MARK_IF_UNKNOWN, H5O_UPDATE_TIME, &fsinfo) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, FAIL, "unable to update free-space info header message") } /* end if */ } /* end if */ else { /* Check for creating an "old-style" driver info block */ if (driver_size > 0) { /* Sanity check */ HDassert(H5F_addr_defined(sblock->driver_addr)); /* Allocate space for the driver info */ if (NULL == (drvinfo = (H5O_drvinfo_t *)H5MM_calloc(sizeof(H5O_drvinfo_t)))) HGOTO_ERROR(H5E_FILE, H5E_CANTALLOC, FAIL, "memory allocation failed for driver info message") /* Set up driver info message */ /* (NOTE: All the actual information (name & driver information) is * actually based on the VFD info in the file handle and * will be encoded by the VFD's 'encode' callback, so it * doesn't need to be set here. -QAK, 7/20/2013 */ H5_CHECKED_ASSIGN(drvinfo->len, size_t, H5FD_sb_size(f->shared->lf), hsize_t); /* Insert driver info block into cache */ if (H5AC_insert_entry(f, H5AC_DRVRINFO, sblock->driver_addr, drvinfo, H5AC__PIN_ENTRY_FLAG | H5AC__FLUSH_LAST_FLAG | H5AC__FLUSH_COLLECTIVELY_FLAG) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTINS, FAIL, "can't add driver info block to cache") drvinfo_in_cache = TRUE; f->shared->drvinfo = drvinfo; } /* end if */ else HDassert(!H5F_addr_defined(sblock->driver_addr)); } /* end if */ done: /* Reset the ring in the API context */ if (orig_ring != H5AC_RING_INV) H5AC_set_ring(orig_ring, NULL); /* Close superblock extension, if it was created */ if (ext_created && H5F__super_ext_close(f, &ext_loc, ext_created) < 0) HDONE_ERROR(H5E_FILE, H5E_CANTRELEASE, FAIL, "unable to close file's superblock extension") /* Cleanup on failure */ if (ret_value < 0) { /* Check if the driver info block has been allocated yet */ if (drvinfo) { /* Check if we've cached it already */ if (drvinfo_in_cache) { /* Unpin drvinfo in cache */ if (H5AC_unpin_entry(drvinfo) < 0) HDONE_ERROR(H5E_FILE, H5E_CANTUNPIN, FAIL, "unable to unpin driver info") /* Evict the driver info block from the cache */ if (H5AC_expunge_entry(f, H5AC_DRVRINFO, sblock->driver_addr, H5AC__NO_FLAGS_SET) < 0) HDONE_ERROR(H5E_FILE, H5E_CANTEXPUNGE, FAIL, "unable to expunge driver info block") } /* end if */ else /* Free driver info block */ H5MM_xfree(drvinfo); } /* end if */ /* Check if the superblock has been allocated yet */ if (sblock) { /* Check if we've cached it already */ if (sblock_in_cache) { /* Unpin superblock in cache */ if (H5AC_unpin_entry(sblock) < 0) HDONE_ERROR(H5E_FILE, H5E_CANTUNPIN, FAIL, "unable to unpin superblock") /* Evict the superblock from the cache */ if (H5AC_expunge_entry(f, H5AC_SUPERBLOCK, (haddr_t)0, H5AC__NO_FLAGS_SET) < 0) HDONE_ERROR(H5E_FILE, H5E_CANTEXPUNGE, FAIL, "unable to expunge superblock") } /* end if */ else /* Free superblock */ if (H5F__super_free(sblock) < 0) HDONE_ERROR(H5E_FILE, H5E_CANTFREE, FAIL, "unable to destroy superblock") /* Reset variables in file structure */ f->shared->sblock = NULL; } /* end if */ } /* end if */ FUNC_LEAVE_NOAPI_TAG(ret_value) } /* end H5F__super_init() */ /*------------------------------------------------------------------------- * Function: H5F_eoa_dirty * * Purpose: Mark the file's EOA info dirty * * Return: Success: non-negative on success * Failure: Negative * * Programmer: Quincey Koziol * January 4, 2017 * *------------------------------------------------------------------------- */ herr_t H5F_eoa_dirty(H5F_t *f) { herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_NOAPI(FAIL) /* Sanity check */ HDassert(f); HDassert(f->shared); HDassert(f->shared->sblock); /* Mark superblock dirty in cache, so change to EOA will get encoded */ if (H5F_super_dirty(f) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTMARKDIRTY, FAIL, "unable to mark superblock as dirty") /* If the driver information block exists, mark it dirty as well * so that the change in eoa will be reflected there as well if * appropriate. */ if (f->shared->drvinfo) { if (H5AC_mark_entry_dirty(f->shared->drvinfo) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTMARKDIRTY, FAIL, "unable to mark drvinfo as dirty") } /* end if */ /* If the driver info is stored as a message, update that instead */ else if (f->shared->drvinfo_sb_msg_exists) { if (H5F__update_super_ext_driver_msg(f) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTMARKDIRTY, FAIL, "unable to mark drvinfo message as dirty") } done: FUNC_LEAVE_NOAPI(ret_value) } /* H5F_eoa_dirty() */ /*------------------------------------------------------------------------- * Function: H5F_super_dirty * * Purpose: Mark the file's superblock dirty * * Return: Success: non-negative on success * Failure: Negative * * Programmer: Quincey Koziol * August 14, 2009 * *------------------------------------------------------------------------- */ herr_t H5F_super_dirty(H5F_t *f) { herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_NOAPI(FAIL) /* Sanity check */ HDassert(f); HDassert(f->shared); HDassert(f->shared->sblock); /* Mark superblock dirty in cache, so change to EOA will get encoded */ if (H5AC_mark_entry_dirty(f->shared->sblock) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTMARKDIRTY, FAIL, "unable to mark superblock as dirty") done: FUNC_LEAVE_NOAPI(ret_value) } /* H5F_super_dirty() */ /*------------------------------------------------------------------------- * Function: H5F__super_free * * Purpose: Destroyer the file's superblock * * Return: Success: non-negative on success * Failure: Negative * * Programmer: Quincey Koziol * April 1, 2010 * *------------------------------------------------------------------------- */ herr_t H5F__super_free(H5F_super_t *sblock) { FUNC_ENTER_PACKAGE_NOERR /* Sanity check */ HDassert(sblock); /* Free root group symbol table entry, if any */ sblock->root_ent = (H5G_entry_t *)H5MM_xfree(sblock->root_ent); /* Free superblock */ sblock = (H5F_super_t *)H5FL_FREE(H5F_super_t, sblock); FUNC_LEAVE_NOAPI(SUCCEED) } /* H5F__super_free() */ /*------------------------------------------------------------------------- * Function: H5F__super_size * * Purpose: Get storage size of the superblock and superblock extension * * Return: Success: non-negative on success * Failure: Negative * * Programmer: Vailin Choi * July 11, 2007 * *------------------------------------------------------------------------- */ herr_t H5F__super_size(H5F_t *f, hsize_t *super_size, hsize_t *super_ext_size) { H5AC_ring_t orig_ring = H5AC_RING_INV; /* Original ring value */ herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_PACKAGE /* Sanity check */ HDassert(f); HDassert(f->shared); HDassert(f->shared->sblock); /* Set the superblock size */ if (super_size) *super_size = (hsize_t)H5F_SUPERBLOCK_SIZE(f->shared->sblock); /* Set the superblock extension size */ if (super_ext_size) { if (H5F_addr_defined(f->shared->sblock->ext_addr)) { H5O_loc_t ext_loc; /* "Object location" for superblock extension */ H5O_hdr_info_t hdr_info; /* Object info for superblock extension */ /* Set up "fake" object location for superblock extension */ H5O_loc_reset(&ext_loc); ext_loc.file = f; ext_loc.addr = f->shared->sblock->ext_addr; /* Set the ring type in the API context */ H5AC_set_ring(H5AC_RING_SBE, &orig_ring); /* Get object header info for superblock extension */ if (H5O_get_hdr_info(&ext_loc, &hdr_info) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTGET, FAIL, "unable to retrieve superblock extension info") /* Set the superblock extension size */ *super_ext_size = hdr_info.space.total; } /* end if */ else /* Set the superblock extension size to zero */ *super_ext_size = (hsize_t)0; } /* end if */ done: /* Reset the ring in the API context */ if (orig_ring != H5AC_RING_INV) H5AC_set_ring(orig_ring, NULL); FUNC_LEAVE_NOAPI(ret_value) } /* H5F__super_size() */ /*------------------------------------------------------------------------- * Function: H5F__super_ext_write_msg() * * Purpose: Write the message with ID to the superblock extension * * Return: Non-negative on success/Negative on failure * * Programmer: Vailin Choi; Feb 2009 * *------------------------------------------------------------------------- */ herr_t H5F__super_ext_write_msg(H5F_t *f, unsigned id, void *mesg, hbool_t may_create, unsigned mesg_flags) { H5AC_ring_t orig_ring = H5AC_RING_INV; /* Original ring value */ hbool_t ext_created = FALSE; /* Whether superblock extension was created */ hbool_t ext_opened = FALSE; /* Whether superblock extension was opened */ H5O_loc_t ext_loc; /* "Object location" for superblock extension */ htri_t status; /* Indicate whether the message exists or not */ herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_PACKAGE /* Sanity checks */ HDassert(f); HDassert(f->shared); HDassert(f->shared->sblock); /* Set the ring type in the API context */ H5AC_set_ring(H5AC_RING_SBE, &orig_ring); /* Open/create the superblock extension object header */ if (H5F_addr_defined(f->shared->sblock->ext_addr)) { if (H5F__super_ext_open(f, f->shared->sblock->ext_addr, &ext_loc) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTOPENOBJ, FAIL, "unable to open file's superblock extension") } /* end if */ else { HDassert(may_create); if (H5F__super_ext_create(f, &ext_loc) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTCREATE, FAIL, "unable to create file's superblock extension") ext_created = TRUE; } /* end else */ HDassert(H5F_addr_defined(ext_loc.addr)); ext_opened = TRUE; /* Check if message with ID does not exist in the object header */ if ((status = H5O_msg_exists(&ext_loc, id)) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTGET, FAIL, "unable to check object header for message or message exists") /* Check for creating vs. writing */ if (may_create) { if (status) HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "Message should not exist") /* Create the message with ID in the superblock extension */ if (H5O_msg_create(&ext_loc, id, (mesg_flags | H5O_MSG_FLAG_DONTSHARE), H5O_UPDATE_TIME, mesg) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTGET, FAIL, "unable to create the message in object header") } /* end if */ else { if (!status) HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "Message should exist") /* Update the message with ID in the superblock extension */ if (H5O_msg_write(&ext_loc, id, (mesg_flags | H5O_MSG_FLAG_DONTSHARE), H5O_UPDATE_TIME, mesg) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTGET, FAIL, "unable to write the message in object header") } /* end else */ done: /* Reset the ring in the API context */ if (orig_ring != H5AC_RING_INV) H5AC_set_ring(orig_ring, NULL); /* Close the superblock extension, if it was opened */ if (ext_opened && H5F__super_ext_close(f, &ext_loc, ext_created) < 0) HDONE_ERROR(H5E_FILE, H5E_CANTRELEASE, FAIL, "unable to close file's superblock extension") /* Mark superblock dirty in cache, if superblock extension was created */ if (ext_created && H5AC_mark_entry_dirty(f->shared->sblock) < 0) HDONE_ERROR(H5E_FILE, H5E_CANTMARKDIRTY, FAIL, "unable to mark superblock as dirty") FUNC_LEAVE_NOAPI(ret_value) } /* H5F__super_ext_write_msg() */ /*------------------------------------------------------------------------- * Function: H5F__super_ext_remove_msg * * Purpose: Remove the message with ID from the superblock extension * * Return: Non-negative on success/Negative on failure * * Programmer: Vailin Choi; Feb 2009 * *------------------------------------------------------------------------- */ herr_t H5F__super_ext_remove_msg(H5F_t *f, unsigned id) { H5AC_ring_t orig_ring = H5AC_RING_INV; /* Original ring value */ H5O_loc_t ext_loc; /* "Object location" for superblock extension */ hbool_t ext_opened = FALSE; /* Whether the superblock extension was opened */ int null_count = 0; /* # of null messages */ htri_t status; /* Indicate whether the message exists or not */ herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_PACKAGE /* Make sure that the superblock extension object header exists */ HDassert(H5F_addr_defined(f->shared->sblock->ext_addr)); /* Set the ring type in the API context */ H5AC_set_ring(H5AC_RING_SBE, &orig_ring); /* Open superblock extension object header */ if (H5F__super_ext_open(f, f->shared->sblock->ext_addr, &ext_loc) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTRELEASE, FAIL, "error in starting file's superblock extension") ext_opened = TRUE; /* Check if message with ID exists in the object header */ if ((status = H5O_msg_exists(&ext_loc, id)) < 0) HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "unable to check object header for message") else if (status) { /* message exists */ H5O_hdr_info_t hdr_info; /* Object header info for superblock extension */ /* Remove the message */ if (H5O_msg_remove(&ext_loc, id, H5O_ALL, TRUE) < 0) HGOTO_ERROR(H5E_OHDR, H5E_CANTDELETE, FAIL, "unable to delete free-space manager info message") /* Get info for the superblock extension's object header */ if (H5O_get_hdr_info(&ext_loc, &hdr_info) < 0) HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "unable to retrieve superblock extension info") /* If the object header is an empty base chunk, remove superblock extension */ if (hdr_info.nchunks == 1) { if ((null_count = H5O_msg_count(&ext_loc, H5O_NULL_ID)) < 0) HGOTO_ERROR(H5E_SYM, H5E_CANTCOUNT, FAIL, "unable to count messages") else if ((unsigned)null_count == hdr_info.nmesgs) { HDassert(H5F_addr_defined(ext_loc.addr)); if (H5O_delete(f, ext_loc.addr) < 0) HGOTO_ERROR(H5E_SYM, H5E_CANTCOUNT, FAIL, "unable to count messages") f->shared->sblock->ext_addr = HADDR_UNDEF; } /* end else-if */ } /* end if */ } /* end if */ done: /* Reset the ring in the API context */ if (orig_ring != H5AC_RING_INV) H5AC_set_ring(orig_ring, NULL); /* Close superblock extension object header, if opened */ if (ext_opened && H5F__super_ext_close(f, &ext_loc, FALSE) < 0) HDONE_ERROR(H5E_FILE, H5E_CANTRELEASE, FAIL, "unable to close file's superblock extension") FUNC_LEAVE_NOAPI(ret_value) } /* H5F__super_ext_remove_msg() */ /*------------------------------------------------------------------------- * Function: H5O__fsinfo_set_version * * Purpose: Set the version to encode the fsinfo message with. * * Return: Non-negative on success/Negative on failure * * Programmer: Vailin Choi; June 2019 * *------------------------------------------------------------------------- */ herr_t H5O__fsinfo_set_version(H5F_t *f, H5O_fsinfo_t *fsinfo) { unsigned version; /* Message version */ herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_NOAPI(FAIL) /* Sanity check */ HDassert(f); HDassert(fsinfo); version = H5O_FSINFO_VERSION_1; /* Upgrade to the version indicated by the file's low bound if higher */ if (H5O_fsinfo_ver_bounds[H5F_LOW_BOUND(f)] != H5O_INVALID_VERSION) version = MAX(version, H5O_fsinfo_ver_bounds[H5F_LOW_BOUND(f)]); /* Version bounds check */ if (H5O_fsinfo_ver_bounds[H5F_HIGH_BOUND(f)] == H5O_INVALID_VERSION || version > H5O_fsinfo_ver_bounds[H5F_HIGH_BOUND(f)]) HGOTO_ERROR(H5E_FILE, H5E_BADRANGE, FAIL, "File space info message's version out of bounds") /* Set the message version */ fsinfo->version = version; done: FUNC_LEAVE_NOAPI(ret_value) } /* end H5O__fsinfo_set_version() */