From 194f8d98266b62d4e9df16da1548f260a2b1ad32 Mon Sep 17 00:00:00 2001
From: Aaron Kimball <aaron@cloudera.com>
Date: Wed, 6 Jan 2010 12:12:47 -0800
Subject: [PATCH] MAPREDUCE-1131. Using profilers other than hprof can cause JobClient to report job failure.

---
 src/mapred/org/apache/hadoop/mapred/JobClient.java |   42 ++++++++++-
 .../apache/hadoop/mapreduce/TestMRJobClient.java   |   83 ++++++++++++++++++++
 2 files changed, 124 insertions(+), 1 deletions(-)
 create mode 100644 src/test/org/apache/hadoop/mapreduce/TestMRJobClient.java

diff --git a/src/mapred/org/apache/hadoop/mapred/JobClient.java b/src/mapred/org/apache/hadoop/mapred/JobClient.java
index bf223f2..d682ebc 100644
--- a/src/mapred/org/apache/hadoop/mapred/JobClient.java
+++ b/src/mapred/org/apache/hadoop/mapred/JobClient.java
@@ -1299,6 +1299,46 @@ public class JobClient extends Configured implements MRConstants, Tool  {
   }
   
   /**
+   * @return true if the profile parameters indicate that this is using
+   * hprof, which generates profile files in a particular location
+   * that we can retrieve to the client.
+   */
+  private boolean shouldDownloadProfile(JobConf conf) {
+    // Check the argument string that was used to initialize profiling.
+    // If this indicates hprof and file-based output, then we're ok to
+    // download.
+    String profileParams = conf.getProfileParams();
+
+    if (null == profileParams) {
+      return false;
+    }
+
+    // Split this on whitespace.
+    String [] parts = profileParams.split("[ \\t]+");
+
+    // If any of these indicate hprof, and the use of output files, return true.
+    boolean hprofFound = false;
+    boolean fileFound = false;
+    for (String p : parts) {
+      if (p.startsWith("-agentlib:hprof") || p.startsWith("-Xrunhprof")) {
+        hprofFound = true;
+
+        // This contains a number of comma-delimited components, one of which
+        // may specify the file to write to. Make sure this is present and
+        // not empty.
+        String [] subparts = p.split(",");
+        for (String sub : subparts) {
+          if (sub.startsWith("file=") && sub.length() != "file=".length()) {
+            fileFound = true;
+          }
+        }
+      }
+    }
+
+    return hprofFound && fileFound;
+  }
+
+  /**
    * Monitor a job and print status in real-time as progress is made and tasks 
    * fail.
    * @param conf the job's configuration
@@ -1335,7 +1375,7 @@ public class JobClient extends Configured implements MRConstants, Tool  {
       eventCounter += events.length;
       for(TaskCompletionEvent event : events){
         TaskCompletionEvent.Status status = event.getTaskStatus();
-        if (profiling && 
+        if (profiling && shouldDownloadProfile(conf) &&
             (status == TaskCompletionEvent.Status.SUCCEEDED ||
                 status == TaskCompletionEvent.Status.FAILED) &&
                 (event.isMap ? mapRanges : reduceRanges).
diff --git a/src/test/org/apache/hadoop/mapreduce/TestMRJobClient.java b/src/test/org/apache/hadoop/mapreduce/TestMRJobClient.java
new file mode 100644
index 0000000..88bc8e6
--- /dev/null
+++ b/src/test/org/apache/hadoop/mapreduce/TestMRJobClient.java
@@ -0,0 +1,83 @@
+/**
+ * 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.hadoop.mapreduce;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.mapred.ClusterMapReduceTestCase;
+import org.apache.hadoop.mapred.JobConf;
+import org.apache.hadoop.mapred.TaskCompletionEvent;
+
+import org.junit.Test;
+
+public class TestMRJobClient extends ClusterMapReduceTestCase {
+
+  @Test
+  public void testMissingProfileOutput() throws Exception {
+    Configuration conf = createJobConf();
+    final String input = "hello1\n";
+
+    // Set a job to be profiled with an empty agentlib parameter.
+    // This will fail to create profile.out files for tasks.
+    // This will succeed by skipping the HTTP fetch of the
+    // profiler output.
+    Job job = MapReduceTestUtil.createJob(conf,
+        getInputDir(), getOutputDir(), 1, 1, input);
+    job.setJobName("disable-profile-fetch");
+    JobConf jobConf = (JobConf) job.getConfiguration();
+    jobConf.setProfileEnabled(true);
+    jobConf.setProfileParams("-agentlib:,verbose=n,file=%s");
+    jobConf.setMaxMapAttempts(1);
+    jobConf.setMaxReduceAttempts(1);
+    job.waitForCompletion(true);
+
+    // Run another job with an hprof agentlib param; verify
+    // that the HTTP fetch works here.
+    Job job2 = MapReduceTestUtil.createJob(conf,
+        getInputDir(), getOutputDir(), 1, 1, input);
+    JobConf jobConf2 = (JobConf) job2.getConfiguration();
+    job2.setJobName("enable-profile-fetch");
+    jobConf2.setProfileEnabled(true);
+    jobConf2.setProfileParams(
+        "-agentlib:hprof=cpu=samples,heap=sites,force=n,"
+        + "thread=y,verbose=n,file=%s");
+    jobConf2.setProfileTaskRange(true, "0-1");
+    jobConf2.setProfileTaskRange(false, "");
+    jobConf2.setMaxMapAttempts(1);
+    jobConf2.setMaxReduceAttempts(1);
+    job2.waitForCompletion(true);
+
+    // Find the first map task, verify that we got its profile output file.
+    TaskCompletionEvent [] completionEvents = job2.getTaskCompletionEvents(0);
+    TaskAttemptID attemptId = null;
+    for (TaskCompletionEvent event : completionEvents) {
+      if (event.isMapTask()) {
+        attemptId = event.getTaskAttemptId();
+        break;
+      }
+    }
+
+    assertNotNull("Could not find a map task attempt", attemptId);
+    File profileOutFile = new File(attemptId.toString() + ".profile");
+    assertTrue("Couldn't find profiler output", profileOutFile.exists());
+    assertTrue("Couldn't remove profiler output", profileOutFile.delete());
+  }
+}
-- 
1.6.0.4

