/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 * * 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 org.apache.brooklyn.qa.longevity; import static com.google.common.base.Strings.isNullOrEmpty; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import org.apache.brooklyn.util.http.HttpTool; import org.apache.brooklyn.util.http.HttpToolResponse; import org.apache.brooklyn.util.stream.StreamGobbler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Objects; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.google.common.io.ByteStreams; public class MonitorUtils { private static final Logger LOG = LoggerFactory.getLogger(MonitorUtils.class); private static volatile int ownPid = -1; /** * Confirm can read from URL. * * @param url */ public static boolean isUrlUp(URL url) { try { HttpToolResponse result = HttpTool.httpGet( HttpTool.httpClientBuilder().trustAll().build(), URI.create(url.toString()), ImmutableMap.<String,String>of()); int statuscode = result.getResponseCode(); if (statuscode != 200) { LOG.info("Error reading URL {}: {}, {}", new Object[]{url, statuscode, result.getReasonPhrase()}); return false; } else { return true; } } catch (Exception e) { LOG.info("Error reading URL {}: {}", url, e); return false; } } public static boolean isPidRunning(int pid) { return isPidRunning(pid, null); } /** * Confirm the given pid is running, and that the the process matches the given regex. * * @param pid * @param regex */ public static boolean isPidRunning(int pid, String regex) { Process process = exec("ps -p " + pid); String out = waitFor(process); if (process.exitValue() > 0) { String err = toString(process.getErrorStream()); LOG.info(String.format("pid %s not running: %s", pid, err)); return false; } if (regex != null) { String regex2 = "^\\s*" + pid + ".*" + regex; boolean found = false; for (String line : out.split("\n")) { if (hasAtLeastOneMatch(line, regex2)) { found = true; break; } } if (!found) { String txt = toString(process.getInputStream()); LOG.info("process did not match regular expression: "+txt); return false; } } return true; } private static boolean hasAtLeastOneMatch(String line, String regex) { return Pattern.matches(".*"+regex+".*", line); } private static String toString(InputStream in){ try { byte[] bytes = ByteStreams.toByteArray(in); return new String(bytes); } catch (IOException e) { throw Throwables.propagate(e); } } public static List<Integer> getRunningPids(String regex) { return getRunningPids(regex, null); } /** * Confirm the given pid is running, and that the the process matches the given regex. * * @param regex * @param excludingRegex */ public static List<Integer> getRunningPids(String regex, String excludingRegex) { Process process = exec("ps ax"); String out = waitFor(process); List<Integer> result = new LinkedList<Integer>(); for (String line : out.split("\n")) { if (excludingRegex != null && hasAtLeastOneMatch(line, excludingRegex)) { continue; } if (hasAtLeastOneMatch(line, regex)) { String[] linesplit = line.trim().split("\\s+"); result.add(Integer.parseInt(linesplit[0])); } } return result; } public static MemoryUsage getMemoryUsage(int pid){ return getMemoryUsage(pid, null,0); } /** * @param pid */ public static MemoryUsage getMemoryUsage(int pid, String clazzRegexOfInterest, int minInstancesOfInterest) { Process process = exec(String.format("jmap -histo %s", pid)); String out = waitFor(process); Map<String, Integer> instanceCounts = Maps.newLinkedHashMap(); long totalInstances=0; long totalMemoryBytes=0; for (String line : out.split("\n")) { if (clazzRegexOfInterest!=null && hasAtLeastOneMatch(line, clazzRegexOfInterest)) { // Format is: // num #instances #bytes class name // 1: 43506 8047096 example.MyClazz String[] parts = line.trim().split("\\s+"); String clazz = parts[3]; int instanceCount = Integer.parseInt(parts[1]); if (instanceCount >= minInstancesOfInterest) { instanceCounts.put(clazz, instanceCount); } } if (hasAtLeastOneMatch(line, "^Total.*")) { String[] parts = line.split("\\s+"); totalInstances = Long.parseLong(parts[1]); totalMemoryBytes = Long.parseLong(parts[2]); } } return new MemoryUsage(totalInstances, totalMemoryBytes, instanceCounts); } public static class MemoryUsage { final long totalInstances; final long totalMemoryBytes; final Map<String, Integer> instanceCounts; MemoryUsage(long totalInstances, long totalMemoryBytes, Map<String, Integer> instanceCounts) { this.totalInstances = totalInstances; this.totalMemoryBytes = totalMemoryBytes; this.instanceCounts = instanceCounts; } public String toString() { return Objects.toStringHelper(this) .add("totalInstances", totalInstances) .add("totalMemoryBytes", totalMemoryBytes) .add("instanceCounts", instanceCounts) .toString(); } public long getTotalInstances() { return totalInstances; } public long getTotalMemoryBytes() { return totalMemoryBytes; } public Map<String, Integer> getInstanceCounts() { return instanceCounts; } } public static List<String> searchLog(File file, String grepOfInterest) { return searchLog(file, grepOfInterest, new LinkedHashSet<String>()); } /** * Find lines in the given file that match given given regex. * * @param file * @param grepOfInterest */ public static List<String> searchLog(File file, String grepOfInterest, Set<String> grepExclusions) { Process process = exec(String.format("grep -E %s %s", grepOfInterest, file.getAbsoluteFile())); String out = waitFor(process); // TODO Annoying that String.split() returns size 1 when empty string; lookup javadoc when back online... if (out.length() == 0) return Collections.<String>emptyList(); List<String> result = new ArrayList<String>(); for (String line : out.trim().split("\n")) { boolean excluded = false; for (String exclusion : grepExclusions) { if (!isNullOrEmpty(exclusion) && hasAtLeastOneMatch(line, exclusion)) { excluded = true; } } if (!excluded) { result.add(line); } } return result; } public static Process exec(String cmd) { LOG.info("executing cmd: " + cmd); try { return Runtime.getRuntime().exec(cmd); } catch (IOException e) { throw Throwables.propagate(e); } } public static class ProcessHasStderr extends IllegalStateException { private static final long serialVersionUID = -937871002993888405L; byte[] stderrBytes; public ProcessHasStderr(byte[] stderrBytes) { this("Process printed to stderr: " + new String(stderrBytes), stderrBytes); } public ProcessHasStderr(String message, byte[] stderrBytes) { super(message); this.stderrBytes = stderrBytes; } } /** * Waits for the given process to complete, consuming its stdout and returning it as a string. * If there is any output on stderr an exception will be thrown. */ public static String waitFor(Process process) { ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); @SuppressWarnings("resource") //Closeable doesn't seem appropriate for StreamGobbler since it isn't expected to be called every time StreamGobbler gobblerOut = new StreamGobbler(process.getInputStream(), bytesOut, null); gobblerOut.start(); ByteArrayOutputStream bytesErr = new ByteArrayOutputStream(); @SuppressWarnings("resource") StreamGobbler gobblerErr = new StreamGobbler(process.getErrorStream(), bytesErr, null); gobblerErr.start(); try { process.waitFor(); gobblerOut.blockUntilFinished(); gobblerErr.blockUntilFinished(); if (bytesErr.size() > 0) { throw new ProcessHasStderr(bytesErr.toByteArray()); } return new String(bytesOut.toByteArray()); } catch (Exception e) { throw Throwables.propagate(e); } finally { if (gobblerOut.isAlive()) gobblerOut.interrupt(); if (gobblerErr.isAlive()) gobblerErr.interrupt(); } } public static int findOwnPid() throws IOException { if (ownPid >= 0) return ownPid; String[] cmd = new String[]{"bash", "-c", "echo $PPID"}; Process process = Runtime.getRuntime().exec(cmd); String out = MonitorUtils.waitFor(process); ownPid = Integer.parseInt(out.trim()); return ownPid; } }