From 3a24083598f398bae03eeda9592e60bdcf572288 Mon Sep 17 00:00:00 2001 From: David Ferrero Date: Tue, 27 Aug 2013 22:48:05 -0400 Subject: [PATCH 1/4] Initial commit of changes in support iOS full stack symbolication when combined with the ruby gem squash_ios_crash_log_symbolication, and the SquareSquash/cocoa fork which attaches the PLCrashLog to the uplift. --- app/models/occurrence.rb | 37 ++++++++++++++++++++++ app/views/additions/crashlog_rendering.rb | 38 +++++++++++++++++++++++ app/views/occurrences/show.html.rb | 9 +++++- 3 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 app/views/additions/crashlog_rendering.rb diff --git a/app/models/occurrence.rb b/app/models/occurrence.rb index c1f8053c..8d8d03e6 100644 --- a/app/models/occurrence.rb +++ b/app/models/occurrence.rb @@ -12,6 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +begin + require 'squash_ios_crash_log_symbolication' + @@SquashIosCrashLogSymbolicationAvailable = true; +rescue LoadError + @@SquashIosCrashLogSymbolicationAvailable = false; +end + # An individual occurrence of a {Bug}, or put another way, a single instance of # an exception occurring and being recorded. Occurrences record all relevant # information about the exception itself and the state of the program when the @@ -383,6 +390,7 @@ class Occurrence < ActiveRecord::Base # Universal message: {presence: true, length: {maximum: 1000}}, backtraces: {type: Array, presence: true}, + crash_log: {type: String, allow_nil: true}, ivars: {type: Hash, allow_nil: true}, user_data: {type: Hash, allow_nil: true}, parent_exceptions: {type: Array, allow_nil: true}, @@ -566,6 +574,12 @@ def additional? user_data.present? || extra_data.present? || ivars.present? end + # @return [true, false] Whether or not this Occurrence has a crash log attached + + def crash_log? + crash_log.present? + end + # @return [true, false] Whether or not this exception occurred as part of an # XMLHttpRequest (Ajax) request. @@ -691,6 +705,29 @@ def symbolicate(symb=nil) end end self.backtraces = bt # refresh the actual JSON + + if (user_data.present? && @@SquashIosCrashLogSymbolicationAvailable ) + begin + Rails.logger.debug "-- SquashIosCrashLogSymbolication.symbolicate_crash called... --" + + self.crash_log = SquashIosCrashLogSymbolication.symbolicate_crash(user_data, SquashIosCrashLogSymbolication.env) + + # if we have a symbolicated crash_log, remove the encoded plcrashlog + if(self.crash_log && self.crash_log.present?) + self.user_data = nil + Rails.logger.debug "-- SquashIosCrashLogSymbolication.symbolicate_crash complete. crash_log contains crash_log, user_data set to nil --" + end + rescue Object => err + # don't get into an infinite loop of notifying Squash + Rails.logger.error "-- ERROR IN symbolicate #{err.object_id} --" + Rails.logger.error "SquashIosCrashLogSymbolication.symbolicate(user_data, #{SquashIosCrashLogSymbolication.env})\n" + Rails.logger.error err + Rails.logger.error err.backtrace.join("\n") + Rails.logger.error @attrs.inspect + Rails.logger.error "-- END ERROR #{err.object_id} --" + raise if Rails.env.test? + end + end end # Like {#symbolicate}, but saves the record. diff --git a/app/views/additions/crashlog_rendering.rb b/app/views/additions/crashlog_rendering.rb new file mode 100644 index 00000000..3bf7115f --- /dev/null +++ b/app/views/additions/crashlog_rendering.rb @@ -0,0 +1,38 @@ +# encoding: utf-8 + +# Copyright 2012 Cerner Corp. +# +# Licensed 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. + +# Adds methods to a view class that allow it to render Crash Logs in a standard +# style. + +module CrashlogRendering + protected + + # Renders a Crash Log. + # + # @param [String] crash_log The Crash Log to render, in the format + # used by {Occurrence}. + + def render_crash_log(crash_log) + + h4 "Crash Log" + div(id:'crash_log') do + text raw ("
#{crash_log}
") + end + end + + +end + diff --git a/app/views/occurrences/show.html.rb b/app/views/occurrences/show.html.rb index 6f6b4f44..618fca80 100644 --- a/app/views/occurrences/show.html.rb +++ b/app/views/occurrences/show.html.rb @@ -23,6 +23,7 @@ module Occurrences # @private class Show < Views::Layouts::Application include BacktraceRendering + include CrashlogRendering needs :project, :environment, :bug, :occurrence @@ -116,7 +117,13 @@ def tab_content end def backtrace_tab - render_backtraces @occurrence.backtraces, 'root' + if @occurrence.crash_log? + # do not convert \n to
and \n\n to

using simple_format() + # instead use

 tags in render_crash_log
+            render_crash_log @occurrence.crash_log
+        else
+          render_backtraces @occurrence.backtraces, 'root'
+        end
       end
 
       def parents_tab

From 7e6a5d375af6e32ef92bffe9b2daaeba3f196d51 Mon Sep 17 00:00:00 2001
From: David Ferrero 
Date: Wed, 28 Aug 2013 12:48:55 -0400
Subject: [PATCH 2/4] This branch was created to preserve the original design,
 but should be considered historical only.

---
 doc/README_FOR_APP.md | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/doc/README_FOR_APP.md b/doc/README_FOR_APP.md
index 76397f64..f62f8c23 100644
--- a/doc/README_FOR_APP.md
+++ b/doc/README_FOR_APP.md
@@ -1,6 +1,16 @@
 Squash: A squarish bug spray
 ============================
 
+PLEASE NOTE: 
+This branch is a fork off SquareSquash/web and was originally submitted as a
+pull request off master branch to SquareSquash/web on Aug 27, 2013. It was
+designed to use squash_ios_crash_log_symbolication gem available at that 
+time on rubygems.org. That gem has since been removed but the full stack 
+iOS/OSX symbolication capability is available in the master branch of this 
+repo. This branch was created to preserve the original design, but should
+be considered historical only.
+
+
 **An open-source project from [Square](http://github.com/square)**
 
 Squash is a collection of tools that help engineers find and kill bugs in their

From 45030452e765864c410d1f622ab9a9ff42a9798a Mon Sep 17 00:00:00 2001
From: David Ferrero 
Date: Wed, 28 Aug 2013 16:15:49 -0400
Subject: [PATCH 3/4] Adds iOS and OS X full stack symbolication to
 SquareSquash/web. If SquareSquash/web is running on OS X, symbolication will
 be done inline upon receipt of the crash info from a client using the forked
 SquareSquash/cocoa project. An alternative scenario is to run the script
 standalone on OS X as: ./script/squash_symbolicate_ios_crash such as in a
 crontjob. In that form, the config files mentioned at the top of the script
 must also be available.

---
 app/models/occurrence.rb                  |   7 +-
 config/symbolication_paths.yml            |  35 +++
 doc/README_FOR_APP.md                     |  10 +-
 lib/squash_ios_crash_log_symbolication.rb | 250 ++++++++++++++++++++++
 script/squash_symbolicate_ios_crash       | 152 +++++++++++++
 5 files changed, 447 insertions(+), 7 deletions(-)
 create mode 100644 config/symbolication_paths.yml
 create mode 100644 lib/squash_ios_crash_log_symbolication.rb
 create mode 100755 script/squash_symbolicate_ios_crash

diff --git a/app/models/occurrence.rb b/app/models/occurrence.rb
index 8d8d03e6..3d62d558 100644
--- a/app/models/occurrence.rb
+++ b/app/models/occurrence.rb
@@ -13,8 +13,11 @@
 #    limitations under the License.
 
 begin
-    require 'squash_ios_crash_log_symbolication'
-    @@SquashIosCrashLogSymbolicationAvailable = true;
+    if(SquashIosCrashLogSymbolication.osx?)
+      @@SquashIosCrashLogSymbolicationAvailable = true;
+    else
+      @@SquashIosCrashLogSymbolicationAvailable = false;
+    end
 rescue LoadError
     @@SquashIosCrashLogSymbolicationAvailable = false;
 end
diff --git a/config/symbolication_paths.yml b/config/symbolication_paths.yml
new file mode 100644
index 00000000..9c148e6b
--- /dev/null
+++ b/config/symbolication_paths.yml
@@ -0,0 +1,35 @@
+---
+# for each environment, provide paths used by the symbolication process
+development:
+
+  # PATH to PLCrashReporter's plcrashutil
+  plcrashutil: /usr/local/bin/plcrashutil
+
+  # PATH TO Xcode's symbolicatecrash script
+  symbolicationcrash: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKit.framework/Versions/A/Resources/symbolicatecrash
+
+  # PATH TO Application symbols - Apple's spotlight will try to locate the Application symbols but give it a hint
+  symbolpath: /Library/
+
+test:
+
+  # PATH to PLCrashReporter's plcrashutil
+  plcrashutil: /usr/local/bin/plcrashutil
+
+  # PATH TO Xcode's symbolicatecrash script
+  symbolicationcrash: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKit.framework/Versions/A/Resources/symbolicatecrash
+
+  # PATH TO Application symbols - Apple's spotlight will try to locate the Application symbols but give it a hint
+  symbolpath: /Library/
+
+production:
+
+  # PATH to PLCrashReporter's plcrashutil
+  plcrashutil: /usr/local/bin/plcrashutil
+
+  # PATH TO Xcode's symbolicatecrash script
+  symbolicationcrash: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKit.framework/Versions/A/Resources/symbolicatecrash
+
+  # PATH TO Application symbols - Apple's spotlight will try to locate the Application symbols but give it a hint
+  symbolpath: /Library/
+
diff --git a/doc/README_FOR_APP.md b/doc/README_FOR_APP.md
index f62f8c23..15e1bf41 100644
--- a/doc/README_FOR_APP.md
+++ b/doc/README_FOR_APP.md
@@ -2,14 +2,14 @@ Squash: A squarish bug spray
 ============================
 
 PLEASE NOTE: 
-This branch is a fork off SquareSquash/web and was originally submitted as a
+This repo is a fork off SquareSquash/web and was originally submitted as a
 pull request off master branch to SquareSquash/web on Aug 27, 2013. It was
 designed to use squash_ios_crash_log_symbolication gem available at that 
 time on rubygems.org. That gem has since been removed but the full stack 
-iOS/OSX symbolication capability is available in the master branch of this 
-repo. This branch was created to preserve the original design, but should
-be considered historical only.
-
+iOS/OSX symbolication capability offered in the gem has been integrated
+into the master brank of this fork. The 
+use_gem_squash_ios_crash_log_symbolication branch was created to preserve
+the original design but should be considered historical only.
 
 **An open-source project from [Square](http://github.com/square)**
 
diff --git a/lib/squash_ios_crash_log_symbolication.rb b/lib/squash_ios_crash_log_symbolication.rb
new file mode 100644
index 00000000..0661a8ab
--- /dev/null
+++ b/lib/squash_ios_crash_log_symbolication.rb
@@ -0,0 +1,250 @@
+#
+# Copyright 2013 Cerner Corporation.
+#
+#    Licensed 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.
+#
+
+require 'YAML'
+require 'base64'
+require 'tempfile'
+require 'Logger'
+
+#
+# The main driver for SquashIosCrashLogSymbolication.
+#
+# Used in conjunction with SquareSquash using the modified SquareSquash Cocoa library.
+# The script leveraging this class will convert and symbolicate plcrashlog formatted
+# user_info values from metadata column for iOS crash occurrences into crash_log value
+# of metadata column in occurrence table within the SquareSquash postgres database.
+#
+# Note: this class requires configuration files:
+#   config/symbolication_paths.yml
+#   config/database.yml
+
+class SquashIosCrashLogSymbolication
+    
+    # Configuration filename
+    ConfigurationFileName = "symbolication_paths.yml"
+    
+    # Database configuration filename
+    DatabaseConnectionConfigFileName = "database.yml"
+
+    #
+    # if we are in a Rails environment, use the Rails logger
+    # otherwise use STDOUT
+    #
+    def self.logger
+        (@logger ||=
+            begin
+                if(Rails)
+                    Rails.logger
+                end
+            rescue Exception => ex
+                Logger.new(STDOUT)
+            end
+        )
+    end
+
+    # Config environment to use:
+    # 'development', 'test', or 'production'
+    def self.env
+        @env ||= ENV['env'] || 'development'
+    end
+
+    #
+    # This method takes the unsymbolicated data, and the environment value
+    # The squash_symbolicate_ios_crash cronscript uses this method in the
+    # symbolication process. The SquareSquash web app can also use this method
+    # if SquareSquash web app is installed and configured on a Mac.
+    #
+    #
+    # Fully symbolicate your iOS crash logs stored in SquareSquash using Xcode and plcrashutil
+    #
+    # Arguments:
+    #   user_data: a hashmap with a key containing 'PLCrashLog' which holds the iOS unsymbolicated, base64-encoded plcrashlog data
+    #   environment: 'development' (default), 'test' or 'production'
+    #
+    # Return:
+    #   crash_log: set to the contents of the symbolicated crash log
+    #
+    def self.symbolicate_crash(user_data, env = 'development')
+        @env = env
+
+        if (user_data.nil?)
+            self.logger.warn 'symbolicate_crash: user_data is nil'
+            return nil
+        end
+
+        # create temp filenames
+        now = Time.now
+        plcrash_filename = 'CrashLog.plcrash.' + now.to_i.to_s
+        crash_log_filename = 'CrashLog.crash.' + now.to_i.to_s
+        crash_log_done_filename = 'CrashLog.crash.done.' + now.to_i.to_s
+        # create temp files
+        pl_crash_log_file = Tempfile.new(plcrash_filename)
+        pl_crash_log_file.binmode # <-- binary mode
+        crash_log_file = Tempfile.new(crash_log_filename)
+        crash_log_done_file = Tempfile.new(crash_log_done_filename)
+
+        # grab PLCrashLog out of 'user_data', decode, and write Tempfile CrashLog.plcrash.XXXXXXX
+        pl_crash_log = Base64.urlsafe_decode64(user_data["PLCrashLog"])
+
+        pl_crash_log_file.write(pl_crash_log)
+        pl_crash_log_file.close
+        self.logger.info "#{pl_crash_log_file.path} written"
+
+        # exec plcrashutil to convert to Xcode crash log format
+        command = "#{SquashIosCrashLogSymbolication.plcrashutil_path} convert --format=ios #{pl_crash_log_file.path} > #{crash_log_file.path}"
+        self.logger.info "executing #{command}..."
+
+        # $? # The exit status of the last process terminated.
+        output = `#{command}`; result=$?
+        if($?.exitstatus == 0)
+            self.logger.info "converted #{pl_crash_log_file.path} to #{crash_log_file.path}"
+
+            # Use symbolicatecrash script from Xcode
+            command = "#{SquashIosCrashLogSymbolication.symbolicatecrash_path} -o #{crash_log_done_file.path} #{crash_log_file.path} #{SquashIosCrashLogSymbolication.symbol_path}"
+            self.logger.info "executing #{command}..."
+            # $? # The exit status of the last process terminated.
+            output = `#{command}`; result=$?
+            if($?.exitstatus == 0)
+                self.logger.info "symbolicated crash log to #{crash_log_done_file.path}"
+            else
+                self.logger.error "failed to symbolicate crash log to #{crash_log_done_file.path} - #{output}"
+            end
+        else
+            self.logger.error "failed to converted #{pl_crash_log_file.path} to #{crash_log_file.path} - #{output}"
+        end
+
+        # now read the symbolicated crash log
+        if (File.exists?("#{crash_log_done_file.path}"))
+            symbolicated_crash_contents = File.read("#{crash_log_done_file.path}",File.size("#{crash_log_done_file.path}"))
+
+            # set crash_log to our symbolicated crashlog contents
+            crash_log = symbolicated_crash_contents
+
+            begin
+                # clean up
+                crash_log_file.close
+                crash_log_done_file.close
+                pl_crash_log_file.unlink
+                crash_log_file.unlink
+                crash_log_done_file.unlink
+
+            rescue Exception => ex
+                self.logger.error "symbolicate_crash file clean up Exception:\n #{ex.to_s}"
+                self.logger.error ex.message
+                self.logger.error ex.backtrace.join("\n")
+            end
+        end
+
+        crash_log # return nil or symbolicated crash log contents
+    end
+
+    #
+    # This method is a wrapper to call bin/squash_symbolicate_ios_crash
+    # It connects to PostgreSQL, selects the unsymbolicated data from the
+    # occurrence table, symbolicates using commandline scripts, then makes
+    # another connection to the database and performs an update, removing
+    # the unsymbolicated data and inserting the symbolicated data.
+    #
+    # Example:
+    #   => squash_symbolicate_ios_crash development
+    #
+    # Arguments:
+    #   environment: 'development', 'test' or 'production'
+    #
+    def self.symbolicate(env = nil)
+        @env = env
+        # exec squash_symbolicate_ios_crash
+        script = File.join("#{ENV['PWD']}", 'bin', 'squash_symbolicate_ios_crash')
+
+        command = "#{script} #{env} 2>&1"
+        puts "executing #{command}"
+
+        # $? # The exit status of the last process terminated.
+        output = `#{command}`; result=$?
+        puts output
+        return result
+    end
+
+    # If this is running on OS X, return true
+    def self.osx?
+        if RUBY_PLATFORM =~ /darwin/i
+            true
+        else
+            false
+        end
+    end
+
+    # Path to Xcode's symbolicatecrash script
+    def self.symbolicatecrash_path
+        symbolicatecrash_path = symbolication_paths_config['symbolicationcrash']
+    end
+
+    # Path to Xcode
+    def self.xcode_path
+        if (self.symbolicatecrash_path =~ /Xcode\.app/)
+            $~.pre_match
+        else
+            nil
+        end
+    end
+
+    # Path to PLCrashReporter's plcrashutil
+    def self.plcrashutil_path
+        symbolication_paths_config['plcrashutil']
+    end
+
+    # Path to Application symbols
+    def self.symbol_path
+        symbolication_paths_config['symbolpath']
+    end
+
+    #
+    # = Configuration
+    #
+
+    # Search LOAD_PATH for config/filename
+    # return path to config
+    def self.config_path(filename)
+        path = $LOAD_PATH.find { |dir|
+            file = File.join(dir, '..', 'config', filename)
+            # puts file
+            File.exists?(file)
+        }
+        path ? File.join(path, '..', 'config') : 'config'
+    end
+
+    # The path to the database configuration file
+    def self.symbolication_database_config_file
+        File.join(config_path(DatabaseConnectionConfigFileName), DatabaseConnectionConfigFileName)
+    end
+
+
+    # The database configuration
+    def self.symbolication_database_config
+        (@database ||= YAML::load(File.read(symbolication_database_config_file)))[env]
+    end
+
+    # The path to the configuration file
+    def self.symbolication_paths_config_file
+        File.join(config_path(ConfigurationFileName), ConfigurationFileName)
+    end
+
+    # The path to the configuration file
+    def self.symbolication_paths_config
+        (@configuration ||= YAML::load(File.read(symbolication_paths_config_file)))[env]
+    end
+end
+
diff --git a/script/squash_symbolicate_ios_crash b/script/squash_symbolicate_ios_crash
new file mode 100755
index 00000000..68ec55a1
--- /dev/null
+++ b/script/squash_symbolicate_ios_crash
@@ -0,0 +1,152 @@
+#!/usr/bin/env ruby
+#
+#
+# Copyright 2013 Cerner Corporation.
+#
+#    Licensed 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.
+#
+#
+# Used in conjunction with SquareSquash using the modified SquareSquash Cocoa library.
+# This script can be used to convert and symbolicate user_info values from metadata 
+# column of occurrences table into crash_log metadata value of metadata column in a 
+# postgres database.
+#
+# Note: this script requires config/symbolication_paths.yml and config/database.yml
+#
+
+require 'pg'
+require 'json'
+require 'Logger'
+require_relative '../lib/squash_ios_crash_log_symbolication.rb'
+
+# load our symbolicationpaths and database config files
+#SquashIosCrashLogSymbolication
+
+# variables
+
+# TODO: config option for logging to a specified file
+@logger = Logger.new(STDOUT)
+@id = nil
+@metadata = nil
+
+# load database config 
+db_config = SquashIosCrashLogSymbolication.symbolication_database_config
+
+# database name, host, user, password
+db_host = db_config['hostname']
+
+if (db_host.nil?)
+  puts 'using db_host = localhost'
+  db_host = 'localhost'
+end
+
+db_user = db_config['username']
+db_name = db_config['database']
+db_pw   = db_config['password']
+
+# connect to postgres
+@select_conn = PG.connect(
+    dbname: db_name,
+    host: db_host,
+    user: db_user,
+    password: db_pw
+)
+
+# separate update connection
+@update_conn = PG.connect(
+    dbname: db_name,
+    host: db_host,
+    user: db_user,
+    password: db_pw
+)
+
+
+def symbolicate_crash_log(data)
+    user_data = data['user_data']
+    crash_log = nil
+
+    begin
+        crash_log = SquashIosCrashLogSymbolication.symbolicate_crash(user_data, nil)
+    rescue Exception => ex
+        @logger.info "symbolicate Exception:\n #{ex.to_s}"
+        @logger.error ex.message
+        @logger.error ex.backtrace.join("\n")
+    end
+
+    if(crash_log)
+
+        # add crash_log field to metadata
+        data['crash_log'] = crash_log
+
+        # remove 'user_data' - no longer needed
+        data.delete('user_data')
+
+        # update metadata column
+        @metadata = data
+
+        begin
+            # update database
+            @logger.info "update occurrences set metadata = xxxx where id = #{@id}..."
+            @update_conn.exec("UPDATE OCCURRENCES SET METADATA = $1 where id = $2", [@metadata.to_json.to_s, @id])
+            @logger.info "update occurrences set metadata = xxxx where id = #{@id} committed"
+        rescue Exception => ex
+            @logger.info "update occurrences set metadata caught Exception:\n #{ex.to_s}"
+            @logger.error ex.message
+            @logger.error ex.backtrace.join("\n")
+        end
+    else
+        @logger.error 'symbolicate_crash: crash_log == nil'
+    end
+end
+
+
+# for each cocoa crash occurrence, try to symbolicate it
+#
+# TODO: add a column with a flag to indicate symbolication is complete so QUERY will run fast
+#
+begin
+  result = @select_conn.exec( "SELECT id, metadata FROM occurrences where client = 'cocoa'" )
+
+  result.each do |row|
+    @id = row['id']
+    @metadata = row['metadata']
+    @logger.info "processing occurrence id=#{@id}"
+
+    data = JSON.parse(@metadata)
+    user_data = data['user_data']
+    crash_log = data['crash_log']
+    # until a boolean column is added, test if crash_log is already present
+    begin
+        if (crash_log && !crash_log.empty?)
+            @logger.info "skipping symbolicated occurrence id=#{@id}"
+        else
+            if(user_data && !user_data.empty?)
+                symbolicate_crash_log(data)
+            else
+                @logger.info "skipping (user_data == nil || empty) occurrence id=#{@id}"
+            end
+        end
+    rescue Exception => ex
+        @logger.info "select loop Exception:\n #{ex.to_s}"
+        @logger.error ex.message
+        @logger.error ex.backtrace.join("\n")
+    end
+  end
+
+rescue Exception => ex
+    @logger.info "begin block Exception:\n #{ex.to_s}"
+    @logger.error ex.message
+    @logger.error ex.backtrace.join("\n")
+    raise ex  # reraise
+end
+

From dfe5b679936439df2eb5585480296400c0fc8383 Mon Sep 17 00:00:00 2001
From: David Ferrero 
Date: Wed, 28 Aug 2013 17:14:01 -0400
Subject: [PATCH 4/4] added Requirements section for using full symbolicaiton
 and updated Copyright to 2013

---
 app/views/additions/crashlog_rendering.rb |  2 +-
 doc/README_FOR_APP.md                     | 24 +++++++++++++----------
 2 files changed, 15 insertions(+), 11 deletions(-)

diff --git a/app/views/additions/crashlog_rendering.rb b/app/views/additions/crashlog_rendering.rb
index 3bf7115f..6bb1acb7 100644
--- a/app/views/additions/crashlog_rendering.rb
+++ b/app/views/additions/crashlog_rendering.rb
@@ -1,6 +1,6 @@
 # encoding: utf-8
 
-# Copyright 2012 Cerner Corp.
+# Copyright 2013 Cerner Corp.
 #
 #    Licensed under the Apache License, Version 2.0 (the "License");
 #    you may not use this file except in compliance with the License.
diff --git a/doc/README_FOR_APP.md b/doc/README_FOR_APP.md
index 15e1bf41..719ae453 100644
--- a/doc/README_FOR_APP.md
+++ b/doc/README_FOR_APP.md
@@ -1,16 +1,6 @@
 Squash: A squarish bug spray
 ============================
 
-PLEASE NOTE: 
-This repo is a fork off SquareSquash/web and was originally submitted as a
-pull request off master branch to SquareSquash/web on Aug 27, 2013. It was
-designed to use squash_ios_crash_log_symbolication gem available at that 
-time on rubygems.org. That gem has since been removed but the full stack 
-iOS/OSX symbolication capability offered in the gem has been integrated
-into the master brank of this fork. The 
-use_gem_squash_ios_crash_log_symbolication branch was created to preserve
-the original design but should be considered historical only.
-
 **An open-source project from [Square](http://github.com/square)**
 
 Squash is a collection of tools that help engineers find and kill bugs in their
@@ -53,6 +43,7 @@ Additional configuration options can be found in the following locations:
 * `config/application.rb`
 * `config/environments/*.rb`
 * `config/environments/*/*.yml`
+* `config/symbolication_paths.yml`
 
 If you don't see what you're looking for in any of those files, you'll probably
 have to change the code to make it work. Don't be afraid -- the code is
@@ -68,6 +59,19 @@ Squash requires the following:
 * The Bundler gem
 * Git 1.7 or newer
 
+To realize full stack symbolication for iOS/OS X crashes, the following
+are additional requirements (either on the computer running Squash or
+on the computer where script/squash_symbolicate_ios_crash will be run):
+
+* OS X
+* Xcode
+* plcrashutil (part of PLCrashReporter)
+* iOS/OS X application .dSYM files
+
+In the latter form, the script/squash_symbolicate_ios_crash, config/databas.yml,
+config/symbolication_paths.yml, and the lib/squash_ios_crash_log_symbolication.rb
+files and folder structure are required.
+
 ### Notes on some of the gem and library choices
 
 **Why do you specifically require PostgreSQL?** Squash uses a lot of