1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.tradefed.invoker.logger; 17 18 import com.android.tradefed.build.IBuildProvider; 19 import com.android.tradefed.device.metric.IMetricCollector; 20 import com.android.tradefed.postprocessor.IPostProcessor; 21 import com.android.tradefed.targetprep.ILabPreparer; 22 import com.android.tradefed.targetprep.ITargetPreparer; 23 import com.android.tradefed.targetprep.multi.IMultiTargetPreparer; 24 import com.android.tradefed.testtype.IRemoteTest; 25 26 import java.util.HashMap; 27 import java.util.LinkedHashSet; 28 import java.util.Map; 29 import java.util.Set; 30 import java.util.concurrent.ConcurrentHashMap; 31 32 /** A utility to track the usage of the different Trade Fedederation objects. */ 33 public class TfObjectTracker { 34 35 public static final String TF_OBJECTS_TRACKING_KEY = "tf_objects_tracking"; 36 private static final Set<Class<?>> TRACKED_CLASSES = new LinkedHashSet<Class<?>>(); 37 38 static { 39 TRACKED_CLASSES.add(IBuildProvider.class); 40 TRACKED_CLASSES.add(IMetricCollector.class); 41 TRACKED_CLASSES.add(IMultiTargetPreparer.class); 42 TRACKED_CLASSES.add(IPostProcessor.class); 43 TRACKED_CLASSES.add(IRemoteTest.class); 44 TRACKED_CLASSES.add(ILabPreparer.class); 45 TRACKED_CLASSES.add(ITargetPreparer.class); 46 } 47 TfObjectTracker()48 private TfObjectTracker() {} 49 50 private static final Map<ThreadGroup, Map<String, Long>> mPerGroupUsage = 51 new ConcurrentHashMap<ThreadGroup, Map<String, Long>>(); 52 53 /** Count the occurrence of a give class and its super classes until the Tradefed interface. */ countWithParents(Class<?> object)54 public static void countWithParents(Class<?> object) { 55 if (!count(object)) { 56 return; 57 } 58 // Track all the super class until not a TF interface to get a full picture. 59 countWithParents(object.getSuperclass()); 60 } 61 62 /** 63 * Count explicitly one class and its occurrences 64 * 65 * @param className The object to track 66 * @param occurrences current num of known occurrences 67 */ directCount(String className, long occurrences)68 public static void directCount(String className, long occurrences) { 69 ThreadGroup group = Thread.currentThread().getThreadGroup(); 70 if (mPerGroupUsage.get(group) == null) { 71 mPerGroupUsage.put(group, new ConcurrentHashMap<>()); 72 } 73 Map<String, Long> countMap = mPerGroupUsage.get(group); 74 long count = 0; 75 if (countMap.get(className) != null) { 76 count = countMap.get(className); 77 } 78 count += occurrences; 79 countMap.put(className, count); 80 } 81 82 /** 83 * Count the current occurrence only if it's part of the tracked objects. 84 * 85 * @param object The object to track 86 * @return True if the object was tracked, false otherwise. 87 */ count(Class<?> object)88 private static boolean count(Class<?> object) { 89 ThreadGroup group = Thread.currentThread().getThreadGroup(); 90 String qualifiedName = object.getName(); 91 92 boolean tracked = false; 93 for (Class<?> classTracked : TRACKED_CLASSES) { 94 if (classTracked.isAssignableFrom(object)) { 95 tracked = true; 96 break; 97 } 98 } 99 if (!tracked) { 100 return false; 101 } 102 // Don't track internal classes for now but return true to track subclass if needed. 103 if (qualifiedName.contains("$")) { 104 return true; 105 } 106 if (mPerGroupUsage.get(group) == null) { 107 mPerGroupUsage.put(group, new ConcurrentHashMap<>()); 108 } 109 Map<String, Long> countMap = mPerGroupUsage.get(group); 110 long count = 0; 111 if (countMap.get(qualifiedName) != null) { 112 count = countMap.get(qualifiedName); 113 } 114 count++; 115 countMap.put(qualifiedName, count); 116 return true; 117 } 118 119 /** Returns the usage of the tracked objects. */ getUsage()120 public static Map<String, Long> getUsage() { 121 ThreadGroup group = Thread.currentThread().getThreadGroup(); 122 if (mPerGroupUsage.get(group) == null) { 123 mPerGroupUsage.put(group, new ConcurrentHashMap<>()); 124 } 125 return new HashMap<>(mPerGroupUsage.get(group)); 126 } 127 128 /** Stop tracking the current invocation. This is called automatically by the harness. */ clearTracking()129 public static void clearTracking() { 130 ThreadGroup group = Thread.currentThread().getThreadGroup(); 131 mPerGroupUsage.remove(group); 132 } 133 } 134