Alan Scarpa - Big Nerd Ranch Wed, 01 Jun 2022 19:32:13 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.5 Own Your Crash Logs with PLCrashReporter: Part 3 https://bignerdranch.com/blog/own-your-crash-logs-with-plcrashreporter-part-3/ Tue, 12 Jan 2021 13:00:55 +0000 https://www.bignerdranch.com/?p=4663 In the third post of our PLCrashReporter series, we will walk through how to get PLCrashReporter up and running on your machine. And make sure to check out Part 1 and Part 2 of our series if you haven’t already. Walkthrough It’s time to install PLCrashReporter and get your first crash report! Step 1: Download […]

The post Own Your Crash Logs with PLCrashReporter: Part 3 appeared first on Big Nerd Ranch.

]]>
In the third post of our PLCrashReporter series, we will walk through how to get PLCrashReporter up and running on your machine. And make sure to check out Part 1 and Part 2 of our series if you haven’t already.

Walkthrough

It’s time to install PLCrashReporter and get your first crash report!

Step 1: Download the Library

First, you need to download the latest release of PLCrashReporter to your computer. As of this writing, 1.5.1 is the latest release. Visit the PLCrashReporter releases page and download PLCrashReporter-1.5.1.zip.

You may have noticed another zip file with the word “Static” included in its filename. This is the static library version of PLCrashReporter which we won’t use for this walkthrough. We’ll use the dynamic framework. To learn more about dynamic frameworks versus static libraries check out this BNR blog post on libraries vs frameworks.

Step 2: Set Up the Project

Now that you have the library downloaded, it’s time to put it in an Xcode project.

Open Xcode 12 and create a new iOS app project for this walkthrough. Make sure to use the Swift language with SwiftUI for your app interface, but select to use a UIKit App Delegate for its lifecycle. Name your app “PLCExample.”

Unzip PLCrashReporter-1.5.1.zip and go into the “iOS Framework” folder where you’ll see an item named CrashReporter.framework. Copy and paste it into your project’s directory.

Finally, drag the newly-pasted framework from your project’s directory into your Xcode project. It should look like this:

framework on pl crash reporterNow PLCrashReporter is in your project!

Step 3: Add a Bridging Header

Because PLCrashReporter is an Objective-C library, you’ll need to set up a bridging header before you can start playing with it in your Swift project.

The easiest way to create a bridging header is to create a new Objective-C class in Xcode. Xcode will offer to set up the bridging header for you so you just need to accept its offer, then delete the Objective-C class files while keeping the bridging header.

Once you have your bridging header, open it up and add #import <CrashReporter/CrashReporter.h> This exposes PLCrashReporter to your Swift project.

Step 4: Enable the Crash Reporter

It’s time to start writing some code. To kick things off, you need to enable PLCrashReporter. Open up your AppDelegate and put the following in didFinishLaunchingWithOptions:

let reporter = PLCrashReporter()
reporter.enable()

That’s it! You’ve got PLCrashReporter up and running, ready to record crashes. Time to buckle up and crash your app. We will cover this in our next post!

The post Own Your Crash Logs with PLCrashReporter: Part 3 appeared first on Big Nerd Ranch.

]]>
Own Your Crash Logs with PLCrashReporter: Part 2 https://bignerdranch.com/blog/own-your-crash-logs-with-plcrashreporter-part-2/ Mon, 21 Dec 2020 17:00:01 +0000 https://www.bignerdranch.com/?p=4642 In part two of our PLCrashReporter series, we will examine how crashes are created and learn more about specific crash types.

The post Own Your Crash Logs with PLCrashReporter: Part 2 appeared first on Big Nerd Ranch.

]]>
Creating Crash Logs

This is part two of our PLCrashReporter series. In this post, we will examine how crashes are created and learn more about specific crash types.

A crash handler has a three-phase life cycle:

  1. Preparing to handle crashes – There are a lot of secondary responsibilities to account for, but the essential task of this phase is to register a function (or functions) with the OS that will be executed if and when a crash occurs.
  2. Handling an actual crash – As a crash occurs, the code that was registered during the first phase is executed. During this phase, information about the nature of the crash (e.g., the call stack for the crashed thread) is captured. This information is written to disk for later use.
  3. Recovering a crash log – On iOS this phase does not occur until the user launches the app again. Without a subsequent launch, there is no opportunity for an in-process crash reporter to process the crash log. On macOS, it is possible for an app to run separate processes that detect and report crashes.

Next, let’s look closer at the first phase. How does one prepare to intercept crashes? To answer that, we need to look at how crashes are propagated. On Apple platforms, there are two pathways via which application crashes flow: POSIX signals and Mach exceptions.

POSIX Signals

First up are POSIX signals. When an illegal instruction or a request for termination occurs, the kernel sends a POSIX signal to the offending thread. These signals have a shortlist of usual suspects:

  • SIGSEGV – Memory errors
  • SIGILL – Illegal instructions
  • SIGABRT – Usually when the process itself calls abort()
  • SIGKILL – For example when you issue the killall -9 command in a shell. That “9” is the value of SIGKILL.

Once that signal is delivered, the OS terminates the process. Between when the signal is sent and the process is terminated, any signal handlers that were registered for the process are given an opportunity to respond.

Mach Exceptions

Apple’s operating systems run atop a Darwin kernel. Darwin is a descendant of the Mach kernel. Mach kernels, much like Darwin, use exception messages, rather than POSIX signals, to communicate about unexpected errors in the program flow. Mach exceptions are messages sent over IPC ports, which can be subscribed to by any interested observer with sufficient permissions for the process they’re interested in.

On Darwin, Mach exception messages are actually the underlying mechanism beneath the implementation of POSIX signals. Darwin registers a Mach exception handler that reflects Mach exceptions into POSIX signals. This is why, for example, when you look at a memory access error crash the description of the crash includes EXC_BAD_ACCESS (SIGSEGV), the Mach exception and the POSIX signal respectively.

It is possible for your app to register its own Mach exception server, but a thorough exception server implementation requires use of undocumented or private API and is fraught with even more peril than writing your own POSIX signal handler.

When writing a custom crash logger, you have to decide which of these mechanisms you will use to intercept crashes. While this might seem like a purely academic choice, there is at least one salient edge case: POSIX signal handlers are run on the crashed thread, not a separate thread, thus using the same stack as the crashed thread. If that thread encountered a stack overflow, there will by definition be no available space on top of the stack for your signal handler to be executed, and you will be unable to capture the crash. A Mach exception handler is immune to this edge case because your handler — or, more accurately, your exception server — is listening for exceptions on a dedicated thread, which likely has enough room on its call stack to execute crash-handling code.

PLCrashReporter can use either POSIX signals or Mach exceptions, but the authors strenuously recommend against using a Mach exception server in production code.

Async Signal Safety

Within your crash-handling code, there are truly profound limitations on what APIs you are able to use. There is almost nothing available to you. There is a shortlist of what are called “async-signal-safe” APIs that can be used within a signal handler. Because the heap could be corrupted, they can only use stack memory. Fortunately, they include essential file-system functions like open(2) and write(2), which is how a custom crash handler is able to save a crash log to disk.

The “async” part is misleading from the perspective of a practicing iOS or macOS developer. It doesn’t mean they’re safe for concurrent access from multiple threads. Rather, an API is considered async-signal-safe if it is guaranteed to be fully re-entrant. A crash could occur at any moment during program execution, including somewhere within a call to a function that your crash handler might need to call itself! If your crash handler then called that same function during the course of handling the crash, and that function isn’t async-signal-safe, your crash handler might deadlock, leading to a lost crash log. You need async-signal-safe turtles all the way down.

Some additional things you cannot do because they aren’t async-signal-safe:

  • You cannot allocate memory because malloc isn’t async-signal-safe, and also because the heap might be corrupted. Any memory your crash handler needs to perform its duties must be allocated during app launch when the handler is first registered. Its memory budget is thus fixed and predetermined, even though a crash log could contain any amount of information. Consider how many megabytes of data could be in a single crash log if there are deep call stacks and hundreds of libraries.
  • Signal handlers cannot use Objective-C or Swift. Period. This is because the Objective-C runtime makes extensive use of non-recursive locking and because both Objective-C and Swift provide no way to prevent their runtimes from calling malloc.
  • You cannot dispatch to other threads or queues. Allowing program execution to continue beyond the crash could lead to corruption of the user’s data [2].

Popping Back Off The Stack…

Putting this all together, to write a proper crash reporter requires:

  • Knowledge of UNIX/Mach/Darwin systems beyond the ken of mere mortals
  • Writing mission-critical code without the benefit of any higher-level abstractions
  • Heroic reasoning about code that cannot be debugged with a debugger
  • Being able to finesse many megabytes of data (binary images, call stacks, etc.) through a tiny synchronous window of CPU time
  • Doing all of the above on a shoestring memory budget

By now, you should be sufficiently terrified of writing your own crash logger. If you aren’t, you’re braver than most, or you weren’t paying attention. The rest of us are fortunate that there are existing implementations.

A reliable, well-maintained, open-source library for capturing crashes on Apple platforms is PLCrashReporter. It has changed hands a few times as its ownership hopped from Plausible Labs to Hockey App to Microsoft, but the fundamental design of the library remains the same.

The next post in our series will walk you through adding the PLCrashReporter library to an application so that you can obtain crash logs directly on your device without having to resort to a third party service.

[2]: Landon Fuller, the primary author of PLCrashReporter, gives an example of how allowing program execution to continue after the crash can corrupt or destroy user data in the section “Failure Case: Async-Safety and Data Corruption” of Reliable Crash Reporting.

The post Own Your Crash Logs with PLCrashReporter: Part 2 appeared first on Big Nerd Ranch.

]]>
Own Your Crash Logs with PLCrashReporter: Part 1 https://bignerdranch.com/blog/own-your-crash-logs-with-plcrashreporter-part-1/ Mon, 21 Dec 2020 16:36:10 +0000 https://www.bignerdranch.com/?p=4641 In this four-part series, we will first dive deep into crashes and will provide you with a step-by-step tutorial on how to get PLCrashReporter up and running

The post Own Your Crash Logs with PLCrashReporter: Part 1 appeared first on Big Nerd Ranch.

]]>
Welcome to the PLCrashReporter blog post series. For those unfamiliar with this crash reporting library, it is defined as follows:

PLCrashReporter is a reliable open source library that provides an in-process live crash reporting framework for use on iOS, macOS, and tvOS. The library detects crashes and generates reports to help your investigation and troubleshooting with the information of application, system, process, thread, etc. as well as stack traces.

In the upcoming posts we will first dive deep into crashes by covering:

  • Crash fundamentals
  • Obtaining crash logs
  • Creating crash logs
  • POSIX signals
  • Mach exceptions
  • Async safety

Then, we will provide you with a step-by-step tutorial on how to get PLCrashReporter up and running.

Who This Series is For

You’re curious about crashes – What even is a crash? Obvious questions can be difficult to answer. If there’s a chance you might get asked this in an interview, you’ll be better prepared to answer it after reading.

You want to know how crash logs are created – You’re equally horrified and mesmerized by whatever dark art must be summoned to intercept a crash and finagle it into some useful output.

You’re interested in PLCrashReporter – You’ve heard of PLCrashReporter before but have never peeked under the hood or used it in a project. Now’s your chance. [1]

You’re looking for an alternative to third-party crash reporting services – There are a number of reasons why you wouldn’t want to use a paid crash reporting service. Your institution might not permit closed-source libraries, which most (perhaps all?) third-party crash reporters require. Third-party services cost money, which is a deal-breaker for some small projects. Some have dubious privacy policies that might permit them to gather more than just crash logs. Or a Facebook bug might bring down their infrastructure, leaving your app in the lurch.

You need an alternative to TestFlight and App Store crash reporting for internal distributions – TestFlight gathers crash logs for you, which appear in the Xcode Organizer, but these are, naturally, only available for TestFlight builds. If you’re using Enterprise or Ad Hoc distribution, you need another way to gather development-build crash logs.

If none of these describe you, well, you should keep reading anyway, because what have you got to lose?

Crash Fundamentals

It seems obvious, but let’s ask the question: what is a crash? The video for WWDC 2008 session 414 says it well:

A crash is a sudden termination of your app when it attempts to do something that is not allowed.

There are a number of types of crashes. Let’s take a look at them.

First, there are assertions and preconditions. These occur when code, either in your own source or in libraries that your source is using, deliberately stops the process. For example:

  • Force unwrapping a Swift.Optional – The Swift compiler’s implementation of the ! postfix operator will terminate the process if code attempts to unwrap a .none value.
  • Out-of-bounds Swift.Array access – The standard library will trigger a precondition if the code attempts to access an array using an invalid index.
  • Swift arithmetic errors – Some arithmetic errors, like integer overflow, will cause the app to terminate.
  • Uncaught NSExceptions – Exceptions raised by Objective-C code will cause the app to terminate unless they are explicitly caught. This is particularly pernicious for Swift code since it is unable to catch NSExceptions.
  • Your assertions – Your own assertions and preconditions can cause the app to crash, too, if you’re using any.

Next, there are terminations by the operating system, i.e. something that results in your process receiving the SIGKILL signal. We’ll delve into signals more in the next post. Some examples of these terminations:

  • Watchdog timeout – If your app occupies the main thread without returning for too long, typically for ten seconds or more, then the OS will terminate your application.
  • Device overheating – If the device temperature is climbing too high, the OS will start lopping off the greediest processes to cool down the CPU.
  • Out of memory – If the process attempts to consume more memory than the OS is willing to provide it, the app will be terminated.
  • Invalid code signature – Ever tried launching an app with an expired provisioning profile? Anyone? Anyone? It is terminated the moment you launch it.

Last, but certainly not least, there are memory errors. Our old pal EXC_BAD_ACCESS. Memory errors are some of the most troublesome issues to debug, particularly when multi-threading or manual reference-counting is involved. Some examples:

  • Over-releasing a reference-counted object – Sending the objc_release message to an object that already has a zero reference count is not allowed.
  • Dereferencing a null pointer – Doing so has undefined behavior according to C standards, and in Objective-C will cause a crash due to a memory violation.
  • C array buffer overflow – Reading or writing beyond the bounds of an array is undefined behavior. By itself, it won’t crash, but it could lead to other memory access violations. Note that this differs from the assertions provided by Swift.Array and NSArray, which proactively look for out-of-bounds accesses.
  • Stack overflow – No, not that stack overflow. This stack overflow:

    Stack overflow refers specifically to the case when the execution stack grows beyond the memory that is reserved for it. For example, if you call a function which recursively calls itself without termination, you will cause a stack overflow as each function call creates a new stack frame and the stack will eventually consume more memory than is reserved for it. (Nick Meyer“What is the difference between a stack overflow and buffer overflow?”)

Obtaining Crash Logs

If an app crashes in the woods, but there was no log left behind, would anyone be able to fix it? A detailed, accurate crash log is necessary to correctly diagnose a crash. So what transpires between the moment a process is notified of an impending crash and when the crash log is written to disk? There are, on Apple platforms, two mechanisms that produce crash logs:

  • Out-of-process – The operating system’s own crash handler will capture information about a crashed process and will encode that information in a .crash log. Those logs are stashed in an OS-specific location for later use. This all happens outside of your app’s process. If the crashed app was distributed via Test Flight or the App Store, the crash log will also be uploaded to App Store Connect servers and can be viewed in the Xcode Organizer.
  • In-process – In addition to the operating system’s handler, your app can register its own crash handler. Unlike the system handler, however, your crash handler runs in the same process as the crashed code.

The differences between capturing crashes in-process and capturing them out-of-process is more than superficial. There are profound risks and challenges to overcome to safely and reliably capture crashes in-process. Like all third-party crash reporting tools, PLCrashReporter is obligated to run in-process. These challenges will be a recurring theme across future posts in this series.

Up Next

That’s it for the first post in the series! Next, we will discuss crash log creation, POSIX signals, Mach exceptions, and async-signal safety.

[1] From Plausible Labs website: “PLCrashReporter was the first crash logging solution available for iOS. Today, PLCrashReporter is used by most 3rd-party crash logging services for iOS and macOS, and can be found reporting crashes in the Netflix, Amazon Prime Video, Dropbox, Yahoo Mail, Kindle, and Chase Mobile iOS apps, as well as tens of thousands of other applications. With Microsoft’s purchase of HockeyApp in 2014, PLCrashReporter sits at the core of Microsoft’s crash logging solutions for Apple’s platforms. And in October 2019, Plausible and Microsoft reached an agreement to transfer stewardship of PLCrashReporter to the App Center team at Microsoft, where it will continue to be developed as an open-source project.

The post Own Your Crash Logs with PLCrashReporter: Part 1 appeared first on Big Nerd Ranch.

]]>
What is GraphQL? https://bignerdranch.com/blog/what-is-graphql/ Fri, 06 Nov 2020 16:13:44 +0000 https://www.bignerdranch.com/?p=4604 GraphQL can revolutionize your product by improving performance, reducing friction between development teams, and even helping with documentation. But it's important to understand the tool before deciding that it is the right one for the job.

The post What is GraphQL? appeared first on Big Nerd Ranch.

]]>
GraphQL can revolutionize your product by improving performance, reducing friction between development teams, and even helping with documentation. But it’s important to understand the tool before deciding if it’s the right one for the job. This is the first post in a series that intends to give a strong foundational knowledge to anyone interested in learning if GraphQL is right for their project.

Defining GraphQL

First, let’s explore the basics of GraphQL in order to get a baseline understanding of this query language.

We’ll start with a definition. According to the GraphQL Foundation,  “GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data.”

At its core, GraphQL allows the client to retrieve data from the server. It is a specification that can have different implementations for different platforms, on both the client and server-side. GraphQL also contains a key defining feature that makes it stand out from other web services, which we will explore next.

Once the GraphQL architecture has been set up, the client only has to work with one endpoint, which is typically /graphql. By using this single endpoint, the client can request only the data it needs within the GraphQL query. This enormous degree of flexibility is what sets GraphQL apart because it lets the client control what data it receives. See below for a quick example taken from GraphQL.org:

What the client asks for:

{
    hero {
        name
        height
        mass
    }
}

In its simplest form, this is how the request would look in a POST request body being sent to /graphql:

{
  "query": "hero{name height mass}"
}

And this is what the client would receive:

{
    "hero": {
        "name": "Luke Skywalker",
        "height": 1.72,
        "mass": 77
    }
}

If the above scenario took place in a REST architecture, it is easy to imagine that the client could have asked for the hero and received a lot of unneeded data (over-fetching), or the client may have had to make multiple calls to receive each of these specific attributes (under-fetching). Thanks to GraphQL, we have the advantage of “exactly-right fetching.”

There is much more to GraphQL and its advantages (and disadvantages), but before we get there, let’s briefly explore its origins.

GraphQL History

In 2012, Facebook began its efforts to redesign its native mobile apps due to their poor performance. This effort created new consumers of all their APIs and complex data. Additionally, performance became more critical as large user bases began to grow in regions of the world that lacked fast cellular networks. Milliseconds and kilobytes now mattered more than ever when fetching data.

Unfortunately, this redesign led to frustration. Not only did Facebook have to grapple with the difference between the data they wanted to use and the server queries required to fetch them, but they also had to write a considerable amount of code on both the server and client-side to prepare and parse the data.

These struggles led Facebook to rethink mobile app data-fetching from the perspective of product designers and developers. What was started as an internal project to give more control to the client-side grew into what is now known as GraphQL.

In 2015, it was made open-source. Then, in 2018, the GraphQL project was moved from Facebook to the GraphQL Foundation which is hosted by the non-profit Linux Foundation. A large community of developers and technology companies has grown around it ever since to provide contributions and stewardship to GraphQL.

GraphQL Adopters

Since becoming open-source, a variety of companies have adopted GraphQL. Some of the most recognizable names are:

  • Pinterest
  • Shopify
  • AirBnB
  • PayPal

As you can see, if you choose to implement GraphQL, you will be in good company. According to GraphQL, their users have a combined market cap of $4.08T and funding of $9.34B. For a more complete list of big-name adopters, take a look at this landscape.

Which Team Members Should Be Interested in GraphQL?

Interest in GraphQL can come from several different roles in your organization.

Front-end developers might be interested in implementing it to reduce the number of requests they need to make. Or they might be drawn in by the “exactly-right” fetching we discussed earlier. The front-end devs might also be attracted to the potential for fast iteration on their side. Without the need to ask the server-side to update an endpoint every time the UI changes, the front-end can make changes faster and with less friction.

On the back-end, developers can create a GraphQL server using any common language. This makes GraphQL a strong option to consider. A key reason back-end developers will be interested in GraphQL is that they will not have to spend time making changes to endpoints every time the front-end has changed. Not only this, but GraphQL could also replace legacy REST APIs and reduce the amount of versioning and maintenance required. Another upside for back-end developers is the self-documenting nature of the GraphQL schema.

Project stakeholders would also benefit from learning about GraphQL and what implementing it could mean for a project. If performance is critical, GraphQL would be something to give strong consideration to, especially for apps operating in areas with slower internet speeds. GraphQL could be a valid solution for a project that has growing technical debt due to legacy or ever-evolving REST APIs. A project that has complex systems and microservices could also benefit from the unification that GraphQL can provide.

Tooling & Resources

Due to the growing popularity of GraphQL, there are numerous tools and resources to help you get started.

  • Apollo – The industry-standard GraphQL implementation. According to their website, you can be up and running with a GraphQL data graph in about an hour.
  • GraphiQL – The IDE for GraphQL.
  • Postman – A popular platform for API development that supports GraphQL.
  • Paw – A Mac app that lets you test and describe the APIs you build or consume which now has support for GraphQL.
  • How To GraphQL – An in-depth, open-source tutorial to get GraphQL up and running.

Is GraphQL the Right Choice?

We’ve just begun to scratch the surface of GraphQL and what it can do for your project. But just like any other tool, considerations must be made to determine if it is the right one for the job.

For help in determining whether GraphQL is the right choice, be on the lookout for our next post: Is GraphQL Right For My Project?

The post What is GraphQL? appeared first on Big Nerd Ranch.

]]>