test/lib/jdk/test/lib/jfr/Events.java
author mgronlun
Wed, 30 Oct 2019 19:43:52 +0100
changeset 58863 c16ac7a2eba4
parent 52981 4eff16f47ae2
permissions -rw-r--r--
8226511: Implement JFR Event Streaming Reviewed-by: egahlin, mseledtsov, mgronlun Contributed-by: erik.gahlin@oracle.com, mikhailo.seledtsov@oracle.com, markus.gronlund@oracle.com
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
50113
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
     1
/*
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
     2
 * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
     3
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
     4
 *
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
     5
 * This code is free software; you can redistribute it and/or modify it
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
     6
 * under the terms of the GNU General Public License version 2 only, as
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
     7
 * published by the Free Software Foundation.  Oracle designates this
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
     8
 * particular file as subject to the "Classpath" exception as provided
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
     9
 * by Oracle in the LICENSE file that accompanied this code.
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    10
 *
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    11
 * This code is distributed in the hope that it will be useful, but WITHOUT
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    12
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    13
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    14
 * version 2 for more details (a copy is included in the LICENSE file that
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    15
 * accompanied this code).
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    16
 *
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    17
 * You should have received a copy of the GNU General Public License version
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    18
 * 2 along with this work; if not, write to the Free Software Foundation,
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    19
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    20
 *
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    21
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    22
 * or visit www.oracle.com if you need additional information or have any
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    23
 * questions.
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    24
 */
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    25
package jdk.test.lib.jfr;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    26
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    27
import static jdk.test.lib.Asserts.assertEquals;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    28
import static jdk.test.lib.Asserts.assertFalse;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    29
import static jdk.test.lib.Asserts.assertNotNull;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    30
import static jdk.test.lib.Asserts.assertTrue;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    31
import static jdk.test.lib.Asserts.fail;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    32
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    33
import java.io.File;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    34
import java.io.IOException;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    35
import java.nio.file.Path;
52981
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
    36
import java.time.Duration;
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
    37
import java.time.Instant;
50113
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    38
import java.util.List;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    39
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    40
import jdk.jfr.AnnotationElement;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    41
import jdk.jfr.EventType;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    42
import jdk.jfr.Recording;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    43
import jdk.jfr.SettingDescriptor;
52981
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
    44
import jdk.jfr.Timespan;
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
    45
import jdk.jfr.Timestamp;
50113
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    46
import jdk.jfr.ValueDescriptor;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    47
import jdk.jfr.consumer.RecordingFile;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    48
import jdk.test.lib.Asserts;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    49
import jdk.jfr.consumer.RecordedClass;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    50
import jdk.jfr.consumer.RecordedEvent;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    51
import jdk.jfr.consumer.RecordedObject;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    52
import jdk.jfr.consumer.RecordedThread;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    53
import jdk.jfr.consumer.RecordedThreadGroup;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    54
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    55
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    56
/**
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    57
 * Helper class to verify RecordedEvent content
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    58
 */
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    59
public class Events {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    60
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    61
    public static EventField assertField(RecordedEvent event, String name)  {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    62
        String[] partNames = name.split("\\.");
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    63
        RecordedObject struct = event;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    64
        try {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    65
            for (int i=0; i<partNames.length; ++i) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    66
                final String partName =  partNames[i];
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    67
                final boolean isLastPart = i == partNames.length - 1;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    68
                ValueDescriptor d = getValueDescriptor(struct, partName);
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    69
                if (isLastPart) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    70
                    return new EventField(struct, d);
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    71
                } else {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    72
                    assertTrue(struct.getValue(partName) instanceof RecordedObject, "Expected '" + partName + "' to be a struct");
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    73
                    struct = struct.getValue(partName);
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    74
                }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    75
            }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    76
        } catch (Exception e) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    77
            e.printStackTrace();
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    78
        }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    79
        System.out.printf("Failed event:%n%s%n", event.toString());
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    80
        fail(String.format("Field %s not in event", name));
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    81
        return null;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    82
    }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    83
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    84
    private static RecordedObject getRecordedPackage(final RecordedClass rc) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    85
        if (rc == null) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    86
            throw new RuntimeException("RecordedClass must not be null!");
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    87
        }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    88
        return rc.getValue("package");
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    89
    }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    90
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    91
    private static RecordedObject getRecordedModule(final RecordedObject pkg) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    92
        if (pkg == null) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    93
            // null package is an unnamed module (null)
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    94
            return null;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    95
        }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    96
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    97
        return pkg.getValue("module");
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    98
    }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
    99
    /**
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   100
     * Validates the recored name field
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   101
     *
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   102
     * @param ro should be a Package or a Module
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   103
     * @param targetName name to match
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   104
     */
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   105
    private static boolean isMatchingTargetName(final RecordedObject ro, final String targetName) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   106
        if (ro == null) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   107
            return targetName == null;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   108
        }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   109
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   110
        final String recordedName = ro.getValue("name");
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   111
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   112
        if (recordedName == null) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   113
            return targetName == null;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   114
        }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   115
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   116
        return recordedName.equals(targetName);
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   117
    }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   118
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   119
    public static void assertClassPackage(final RecordedClass rc, final String packageNameTarget) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   120
        final RecordedObject recordedPackage = getRecordedPackage(rc);
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   121
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   122
        if (recordedPackage == null) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   123
            if (packageNameTarget != null) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   124
                throw new RuntimeException("RecordedClass package is null!");
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   125
            }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   126
            return;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   127
        }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   128
        assertTrue(isMatchingTargetName(recordedPackage, packageNameTarget), "mismatched package name! Target is " + packageNameTarget);
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   129
    }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   130
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   131
    public static void assertClassModule(final RecordedClass rc, final String moduleNameTarget) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   132
        final RecordedObject recordedPackage = getRecordedPackage(rc);
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   133
        final RecordedObject recordedModule = getRecordedModule(recordedPackage);
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   134
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   135
        if (recordedModule == null) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   136
            if (moduleNameTarget != null) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   137
                throw new RuntimeException("RecordedClass module is null!");
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   138
            }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   139
            return;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   140
        }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   141
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   142
       assertTrue(isMatchingTargetName(recordedModule, moduleNameTarget), "mismatched module name! Target is " + moduleNameTarget);
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   143
    }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   144
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   145
    private static ValueDescriptor getValueDescriptor(RecordedObject struct, String name) throws Exception {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   146
        List<ValueDescriptor> valueDescriptors = struct.getFields();
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   147
        for (ValueDescriptor d : valueDescriptors) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   148
            if (name.equals(d.getName())) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   149
                return d;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   150
            }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   151
        }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   152
        System.out.printf("Failed struct:%s", struct.toString());
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   153
        fail(String.format("Field %s not in struct", name));
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   154
        return null;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   155
    }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   156
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   157
    public static void hasEvents(List<RecordedEvent> events) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   158
        assertFalse(events.isEmpty(), "No events");
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   159
    }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   160
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   161
    public static void hasEvents(RecordingFile file) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   162
        assertTrue(file.hasMoreEvents(), "No events");
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   163
    }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   164
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   165
    public static void assertEventThread(RecordedEvent event) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   166
        RecordedThread eventThread = event.getThread();
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   167
        if (eventThread == null) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   168
            System.out.printf("Failed event:%n%s%n", event.toString());
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   169
            fail("No thread in event");
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   170
        }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   171
    }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   172
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   173
    public static void assertJavaMethod(RecordedEvent event) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   174
        assertField(event, "method.name").notEmpty();
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   175
        assertField(event, "method.descriptor").notEmpty();
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   176
        assertField(event, "method.modifiers").atLeast(0);
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   177
        assertField(event, "method.hidden");
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   178
        assertField(event, "method.type.name").notEmpty();
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   179
        assertField(event, "method.type.modifiers").atLeast(0);
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   180
    }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   181
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   182
    public static void assertEventThread(RecordedEvent event, Thread thread) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   183
        assertThread(event.getThread(), thread);
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   184
    }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   185
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   186
    public static void assertEventThread(RecordedEvent event, String structName, Thread thread) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   187
        assertThread(assertField(event, structName).notNull().getValue(), thread);
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   188
    }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   189
52981
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   190
    public static void assertDuration(RecordedEvent event, String name, Duration duration) {
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   191
        assertEquals(event.getDuration(name), duration);
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   192
    }
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   193
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   194
    public static void assertInstant(RecordedEvent event, String name, Instant instant) {
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   195
        assertEquals(event.getInstant(name), instant);
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   196
    }
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   197
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   198
    public static void assertMissingValue(RecordedEvent event, String name) {
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   199
       ValueDescriptor v =  event.getEventType().getField(name);
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   200
       if (v.getAnnotation(Timespan.class) != null) {
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   201
           Duration d = event.getDuration(name);
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   202
           assertTrue(d.getSeconds() == Long.MIN_VALUE && d.getNano() == 0);
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   203
           return;
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   204
       }
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   205
       if (v.getAnnotation(Timestamp.class) != null) {
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   206
           Instant instant = event.getInstant(name);
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   207
           assertTrue(instant.equals(Instant.MIN));
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   208
           return;
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   209
       }
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   210
       if (v.getTypeName().equals("double")) {
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   211
           double d = event.getDouble(name);
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   212
           assertTrue(Double.isNaN(d) || d == Double.NEGATIVE_INFINITY);
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   213
           return;
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   214
       }
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   215
       if (v.getTypeName().equals("float")) {
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   216
           float f = event.getFloat(name);
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   217
           assertTrue(Float.isNaN(f) || f == Float.NEGATIVE_INFINITY);
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   218
           return;
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   219
       }
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   220
       if (v.getTypeName().equals("int")) {
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   221
           int i = event.getInt(name);
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   222
           assertTrue(i == Integer.MIN_VALUE);
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   223
           return;
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   224
       }
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   225
       if (v.getTypeName().equals("long")) {
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   226
           assertEquals(event.getLong(name), Long.MIN_VALUE);
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   227
           return;
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   228
       }
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   229
       Object o = event.getValue(name);
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   230
       Asserts.assertNull(o);
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   231
    }
4eff16f47ae2 8165675: Trace event for thread park has incorrect unit for timeout
egahlin
parents: 50113
diff changeset
   232
50113
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   233
    private static void assertThread(RecordedThread eventThread, Thread thread) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   234
        assertNotNull(eventThread, "Thread in event was null");
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   235
        assertEquals(eventThread.getJavaThreadId(), thread.getId(), "Wrong thread id");
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   236
        assertEquals(eventThread.getJavaName(), thread.getName(), "Wrong thread name");
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   237
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   238
        ThreadGroup threadGroup = thread.getThreadGroup();
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   239
        RecordedThreadGroup eventThreadGroup = eventThread.getThreadGroup();
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   240
        assertNotNull(eventThreadGroup, "eventThreadGroup was null");
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   241
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   242
        // Iterate and check all threadGroups
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   243
        while (eventThreadGroup != null) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   244
            final String groupName = eventThreadGroup.getName();
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   245
            if (threadGroup != null) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   246
                assertEquals(groupName, threadGroup.getName(), "Wrong threadGroup name");
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   247
                threadGroup = threadGroup.getParent();
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   248
            } else {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   249
                assertNotNull(groupName, "threadGroup name was null");
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   250
                assertFalse(groupName.isEmpty(), "threadGroup name was empty");
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   251
            }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   252
            eventThreadGroup = eventThreadGroup.getParent();
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   253
        }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   254
    }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   255
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   256
    public static boolean hasField(RecordedEvent event, String name) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   257
        return event.getFields().stream().map(vd -> vd.getName()).anyMatch(s -> s.equals(name));
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   258
    }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   259
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   260
    public static boolean isEventType(RecordedEvent event, String typeName) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   261
        return typeName.equals(event.getEventType().getName());
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   262
    }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   263
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   264
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   265
    /**
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   266
     * Creates a list of events from a recording.
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   267
     *
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   268
     * @param recording recording, not {@code null}
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   269
     * @return an a list, not null
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   270
     * @throws IOException if an event set could not be created due to I/O
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   271
     *         errors.
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   272
     */
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   273
    public static List<RecordedEvent> fromRecording(Recording recording) throws IOException {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   274
        return RecordingFile.readAllEvents(makeCopy(recording));
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   275
    }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   276
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   277
    public static RecordingFile copyTo(Recording r) throws IOException {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   278
        return new RecordingFile(makeCopy(r));
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   279
    }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   280
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   281
    private static Path makeCopy(Recording recording) throws IOException {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   282
        Path p = recording.getDestination();
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   283
        if (p == null) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   284
            File directory = new File(".");
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   285
            // FIXME: Must come up with a way to give human-readable name
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   286
            // this will at least not clash when running parallel.
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   287
            ProcessHandle h = ProcessHandle.current();
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   288
            p = new File(directory.getAbsolutePath(), "recording-" + recording.getId() + "-pid" + h.pid() + ".jfr").toPath();
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   289
            recording.dump(p);
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   290
        }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   291
        return p;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   292
    }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   293
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   294
   public static void hasAnnotation(ValueDescriptor field, Class<? extends java.lang.annotation.Annotation> annotationClass) throws Exception {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   295
       AnnotationElement a = getAnnotation(field, annotationClass);
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   296
       if (a == null) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   297
           throw new Exception("Expected " + annotationClass.getSimpleName() + " on field " + field.getName());
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   298
       }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   299
   }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   300
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   301
   public static void assertAnnotation(ValueDescriptor field, Class<? extends java.lang.annotation.Annotation> annotationClass, String value) throws Exception {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   302
       AnnotationElement a = getAnnotation(field, annotationClass);
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   303
       Object v = a.getValue("value");
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   304
       if (!v.equals(value)) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   305
           throw new Exception("Expected " + annotationClass.getSimpleName() + " on field " + field.getName() + " to have value " + value + ", but got " + v);
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   306
       }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   307
   }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   308
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   309
   // candidate for moving into API
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   310
   public static AnnotationElement getAnnotation(ValueDescriptor v, Class<?> clazz) throws Exception {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   311
      for (AnnotationElement a : v.getAnnotationElements()) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   312
          if (a.getTypeName().equals(clazz.getName())) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   313
              return a;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   314
          }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   315
      }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   316
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   317
      throw new Exception("Could not find annotation " + clazz.getName());
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   318
  }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   319
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   320
   // candidate for moving into API
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   321
   public static AnnotationElement getAnnotationByName(EventType t, String name) throws Exception {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   322
       for (AnnotationElement a : t.getAnnotationElements()) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   323
           if (a.getTypeName().equals(name)) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   324
               return a;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   325
           }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   326
       }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   327
       throw new Exception("Could not find annotation '" + name + " in type " + t.getName());
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   328
   }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   329
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   330
    // candidate for moving into API
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   331
    public static SettingDescriptor getSetting(EventType type, String name) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   332
        for (SettingDescriptor s : type.getSettingDescriptors()) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   333
            if (s.getName().equals(name)) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   334
                return s;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   335
            }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   336
        }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   337
        throw new IllegalArgumentException("Could not setting with name " + name);
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   338
    }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   339
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   340
    public static void hasEvent(Recording r, String name) throws IOException {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   341
        List<RecordedEvent> events = fromRecording(r);
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   342
        Events.hasEvents(events);
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   343
        Events.hasEvent(events, name);
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   344
    }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   345
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   346
    public static void hasEvent(List<RecordedEvent> events, String name) throws IOException {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   347
        if (!containsEvent(events, name)) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   348
            Asserts.fail("Missing event " + name  + " in recording " + events.toString());
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   349
        }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   350
    }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   351
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   352
    public static void hasNotEvent(List<RecordedEvent> events, String name) throws IOException {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   353
        if (containsEvent(events, name)) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   354
            Asserts.fail("Rercording should not contain event " + name  + " " + events.toString());
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   355
        }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   356
    }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   357
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   358
    private static boolean containsEvent(List<RecordedEvent> events, String name) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   359
        for (RecordedEvent event : events) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   360
            if (event.getEventType().getName().equals(name)) {
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   361
                return true;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   362
            }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   363
        }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   364
        return false;
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   365
    }
caf115bb98ad 8199712: Flight Recorder
egahlin
parents:
diff changeset
   366
}