| /* |
| * Copyright (c) 2012, the Dart project authors. |
| * |
| * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except |
| * in compliance with the License. You may obtain a copy of the License at |
| * |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Unless required by applicable law or agreed to in writing, software distributed under the License |
| * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
| * or implied. See the License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package com.google.dart.tools.ui.feedback; |
| |
| import com.google.dart.engine.utilities.instrumentation.HealthUtils; |
| import com.google.dart.engine.utilities.io.PrintStringWriter; |
| import com.google.dart.tools.core.DartCore; |
| import com.google.dart.tools.core.DartCoreDebug; |
| import com.google.dart.tools.core.model.DartSdkManager; |
| import com.google.dart.tools.core.utilities.net.NetUtils; |
| import com.google.dart.tools.ui.DartToolsPlugin; |
| |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.swt.internal.Library; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.ui.IEditorReference; |
| import org.eclipse.ui.PlatformUI; |
| |
| import java.lang.reflect.Field; |
| import java.util.Map; |
| import java.util.zip.CRC32; |
| |
| /** |
| * Provides utility methods for feedback submission. |
| */ |
| @SuppressWarnings("restriction") |
| public class FeedbackUtils { |
| |
| /** |
| * Contains information about the current session |
| */ |
| public static class Stats { |
| public final int numProjects; |
| public final int numEditors; |
| public final int numThreads; |
| public final long maxMem; |
| public final long totalMem; |
| public final long freeMem; |
| public final String indexStats; |
| public final boolean autoRunPubEnabled; |
| |
| public Stats() { |
| this( |
| ResourcesPlugin.getWorkspace().getRoot().getProjects().length, |
| getNumberOfOpenDartEditors(), |
| getNumberOfThreads(), |
| HealthUtils.getMaxMem(), |
| Runtime.getRuntime().totalMemory(), |
| Runtime.getRuntime().freeMemory(), |
| DartCoreDebug.ENABLE_ANALYSIS_SERVER ? null |
| : DartCore.getProjectManager().getIndex().getStatistics(), |
| DartCore.getPlugin().isAutoRunPubEnabled()); |
| } |
| |
| public Stats(int numProjects, int numEditors, int numThreads, long maxMem, long totalMem, |
| long freeMem, String indexStats, boolean autoRunPubEnabled) { |
| this.numProjects = numProjects; |
| this.numEditors = numEditors; |
| this.numThreads = numThreads; |
| this.maxMem = maxMem; |
| this.totalMem = totalMem; |
| this.freeMem = freeMem; |
| this.indexStats = indexStats; |
| this.autoRunPubEnabled = autoRunPubEnabled; |
| } |
| |
| @Override |
| public String toString() { |
| @SuppressWarnings("resource") |
| PrintStringWriter writer = new PrintStringWriter(); |
| |
| writer.print("# projects: "); |
| writer.println(numProjects); |
| |
| writer.print("# open dart files: "); |
| writer.println(countString(numEditors)); |
| |
| writer.println("auto-run pub: " + autoRunPubEnabled); |
| |
| writer.println("localhost resolves to: " + cleanLocalHost(NetUtils.getLoopbackAddress())); |
| |
| writer.print("mem max/total/free: "); |
| writer.print(convertToMeg(maxMem)); |
| writer.print(" / "); |
| writer.print(convertToMeg(totalMem)); |
| writer.print(" / "); |
| writer.print(convertToMeg(freeMem)); |
| writer.println(" MB"); |
| |
| writer.println("thread count: " + countString(numThreads)); |
| |
| if (!DartCoreDebug.ENABLE_ANALYSIS_SERVER) { |
| writer.println("index: " + indexStats); |
| } |
| |
| return writer.toString(); |
| } |
| |
| /** |
| * Converts the given number of bytes to the corresponding number of megabytes (rounded up). |
| */ |
| private long convertToMeg(long numBytes) { |
| return (numBytes + (512 * 1024)) / (1024 * 1024); |
| } |
| |
| private String countString(int count) { |
| return count == -1 ? "<unknown>" : Integer.toString(count); |
| } |
| |
| } |
| |
| /** |
| * Calculate a CRC-32 checksum for a given array of bytes. |
| * |
| * @param data the array of bytes |
| * @return a CRC-32 checksum |
| */ |
| public static String calculateCRC32(byte[] data) { |
| CRC32 crc = new CRC32(); |
| |
| crc.update(data); |
| |
| long val = crc.getValue(); |
| |
| StringBuffer buf = new StringBuffer(); |
| |
| buf.append(toHex((int) (0xFFL & (val >> 24)))); |
| buf.append(toHex((int) (0xFFL & (val >> 16)))); |
| buf.append(toHex((int) (0xFFL & (val >> 8)))); |
| buf.append(toHex((int) (0xFFL & (val)))); |
| |
| return buf.toString().toUpperCase(); |
| } |
| |
| /** |
| * Get a String representation of editor version details. Minimally, this will include the build |
| * id. If a binary mismatch is detected (e.g., editor binary is 32 bit while the OS arch is 64 |
| * bit) the current binary bit info will be appended as well. |
| * |
| * @return a String representation of editor version details |
| */ |
| public static String getEditorVersionDetails() { |
| String binaryDetails = null; |
| try { |
| binaryDetails = binaryMismatch() ? " - " + getBinaryString() : ""; |
| } catch (Exception e) { |
| binaryDetails = "- <unable to detect binary type>"; |
| } |
| return DartToolsPlugin.getVersionString() + " (" + getBuildDate() + ") " + binaryDetails; |
| } |
| |
| /** |
| * Get a String representation of the current OS. |
| * |
| * @return a String representation of the current OS |
| */ |
| public static String getOSName() { |
| return System.getProperty("os.name") + " - " + getOSArch(); |
| } |
| |
| /** |
| * Answer information about the current session |
| */ |
| public static Stats getStats() { |
| try { |
| return new Stats(); |
| } catch (Exception e) { |
| DartCore.logError("Exception obtaining information about the current session", e); |
| return null; |
| } |
| } |
| |
| public static String getWS() { |
| try { |
| return Platform.getWS(); |
| } catch (Throwable ex) { |
| return "unknown"; |
| } |
| } |
| |
| /** |
| * Return a list of the substrings in the given string that are separated by the given separator. |
| * If the given flag is <code>true</code>, the substrings will have leading and trailing |
| * whitespace removed. |
| * |
| * @param string the string to be split |
| * @param separator the separator that delimits substrings |
| * @param trimSubstrings <code>true</code> if substrings should be trimmed |
| * @return the list of substrings that were found |
| */ |
| public static String[] splitString(String string, String separator, boolean trimSubstrings) { |
| String[] results = string.split(separator); |
| |
| if (trimSubstrings) { |
| for (int i = 0; i < results.length; i++) { |
| results[i] = results[i].trim(); |
| } |
| } |
| |
| return results; |
| } |
| |
| static Map<String, String> getSparseOptionsMap() { |
| return Platform.isRunning() ? DartCoreDebug.SPARSE_OPTION_MAP : null; |
| } |
| |
| static boolean isDartiumInstalled() { |
| return isSdkInstalled() && DartSdkManager.getManager().isDartiumInstalled(); |
| } |
| |
| static boolean isSdkInstalled() { |
| return DartSdkManager.getManager().hasSdk(); |
| } |
| |
| private static boolean binaryMismatch() throws Exception { |
| return is64bitOS() != is64bitBinary(); |
| } |
| |
| /** |
| * If localhost doesn't resolve to something we know to be generic, just return "other". This is |
| * used to make sure that we don't accidentally send user identifying information. |
| * |
| * @param host |
| * @return |
| */ |
| private static String cleanLocalHost(String host) { |
| if (host.startsWith("local") || host.startsWith("127.") || host.startsWith("::")) { |
| return host; |
| } else { |
| return "other"; |
| } |
| } |
| |
| private static String getBinaryString() throws Exception { |
| return "*" + (is64bitBinary() ? "64" : "32") + " bit binary*"; |
| } |
| |
| private static String getBuildDate() { |
| return DartCore.getBuildDate(); |
| } |
| |
| private static int getNumberOfOpenDartEditors() { |
| |
| final Integer[] projects = new Integer[] {-1}; |
| |
| Display.getDefault().syncExec(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| int count = 0; |
| for (IEditorReference ref : PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getEditorReferences()) { |
| if (DartCore.isDartLikeFileName(ref.getPartName())) { |
| ++count; |
| } |
| } |
| projects[0] = count; |
| } catch (Throwable e) { |
| //ignore --- -1 value indicates a failure |
| } |
| } |
| }); |
| |
| return projects[0]; |
| } |
| |
| private static int getNumberOfThreads() { |
| try { |
| return Thread.getAllStackTraces().keySet().size(); |
| } catch (Throwable e) { |
| return -1; |
| } |
| } |
| |
| private static String getOSArch() { |
| return System.getProperty("os.arch") + " (" + System.getProperty("os.version") + ")"; |
| } |
| |
| private static boolean is64bitBinary() throws Exception { |
| Class<Library> swtLibraryClass = Library.class; |
| Field is64 = swtLibraryClass.getDeclaredField("IS_64"); |
| is64.setAccessible(true); |
| return is64.getBoolean(swtLibraryClass); |
| } |
| |
| private static boolean is64bitOS() { |
| return getOSArch().contains("64"); |
| } |
| |
| /** |
| * Convert an integer to a hex string. |
| * |
| * @param i an integer |
| * @return a hex representation of the given integer |
| */ |
| private static String toHex(int i) { |
| String str = Integer.toHexString(0xFF & i); |
| |
| if (str.length() < 2) { |
| str = "0" + str; |
| } |
| |
| return str; |
| } |
| } |