mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
These changes fix minor issues that cause compilation errors with our C++ toolchain's stricter default settings: * `Exception.cpp`: The return type of `__cxa_begin_catch` should be `void *`, not `void` (see https://libcxxabi.llvm.org/spec.html). * `Leaks.mm`: `''` is not a valid C char literal; use `'\0'` instead.
233 lines
7.6 KiB
C++
233 lines
7.6 KiB
C++
//===--- Leaks.mm -----------------------------------------------*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// See Leaks.h for a description of this leaks detector.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#if SWIFT_RUNTIME_ENABLE_LEAK_CHECKER
|
|
|
|
#include "Leaks.h"
|
|
#include "swift/Basic/Lazy.h"
|
|
#include "swift/Runtime/HeapObject.h"
|
|
#include "swift/Runtime/Metadata.h"
|
|
#import <objc/objc.h>
|
|
#import <objc/runtime.h>
|
|
#import <Foundation/Foundation.h>
|
|
#include <set>
|
|
#include <cstdio>
|
|
extern "C" {
|
|
#include <pthread.h>
|
|
}
|
|
|
|
using namespace swift;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Extra Interfaces
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
static void _swift_leaks_stopTrackingObjCObject(id obj);
|
|
static void _swift_leaks_startTrackingObjCObject(id obj);
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// State
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// A set of allocated swift only objects that we are tracking for leaks.
|
|
static Lazy<std::set<HeapObject *>> TrackedSwiftObjects;
|
|
|
|
/// A set of allocated objc objects that we are tracking for leaks.
|
|
static Lazy<std::set<id>> TrackedObjCObjects;
|
|
|
|
/// Whether or not we should be collecting objects.
|
|
static bool ShouldTrackObjects = false;
|
|
|
|
/// A course grain lock that we use to synchronize our leak dictionary.
|
|
static pthread_mutex_t LeaksMutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
/// Where we store the dealloc, alloc, and allocWithZone functions we swizzled.
|
|
static IMP old_dealloc_fun;
|
|
static IMP old_alloc_fun;
|
|
static IMP old_allocWithZone_fun;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Init and Deinit Code
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
static void __swift_leaks_dealloc(id self, SEL _cmd) {
|
|
_swift_leaks_stopTrackingObjCObject(self);
|
|
((void (*)(id, SEL))old_dealloc_fun)(self, _cmd);
|
|
}
|
|
|
|
static id __swift_leaks_alloc(id self, SEL _cmd) {
|
|
id result = ((id (*)(id, SEL))old_alloc_fun)(self, _cmd);
|
|
_swift_leaks_startTrackingObjCObject(result);
|
|
return result;
|
|
}
|
|
|
|
static id __swift_leaks_allocWithZone(id self, SEL _cmd, id zone) {
|
|
id result = ((id (*)(id, SEL, id))old_allocWithZone_fun)(self, _cmd, zone);
|
|
_swift_leaks_startTrackingObjCObject(result);
|
|
return result;
|
|
}
|
|
|
|
void _swift_leaks_startTrackingObjects(const char *name) {
|
|
pthread_mutex_lock(&LeaksMutex);
|
|
|
|
// First clear our tracked objects set.
|
|
TrackedSwiftObjects->clear();
|
|
TrackedObjCObjects->clear();
|
|
|
|
// Set that we should track objects.
|
|
ShouldTrackObjects = true;
|
|
|
|
// Swizzle out -(void)dealloc, +(id)alloc, and +(id)allocWithZone: for our
|
|
// custom implementations.
|
|
IMP new_dealloc_fun = (IMP)__swift_leaks_dealloc;
|
|
IMP new_alloc_fun = (IMP)__swift_leaks_alloc;
|
|
IMP new_allocWithZone_fun = (IMP)__swift_leaks_allocWithZone;
|
|
|
|
Method deallocMethod =
|
|
class_getInstanceMethod([NSObject class], @selector(dealloc));
|
|
Method allocMethod = class_getClassMethod([NSObject class], @selector(alloc));
|
|
Method allocWithZoneMethod =
|
|
class_getClassMethod([NSObject class], @selector(allocWithZone:));
|
|
|
|
old_dealloc_fun = method_setImplementation(deallocMethod, new_dealloc_fun);
|
|
old_alloc_fun = method_setImplementation(allocMethod, new_alloc_fun);
|
|
old_allocWithZone_fun =
|
|
method_setImplementation(allocWithZoneMethod, new_allocWithZone_fun);
|
|
|
|
pthread_mutex_unlock(&LeaksMutex);
|
|
}
|
|
|
|
/// This assumes that the LeaksMutex is already being held.
|
|
static void dumpSwiftHeapObjects() {
|
|
const char *comma = "";
|
|
for (HeapObject *Obj : *TrackedSwiftObjects) {
|
|
const HeapMetadata *Metadata = Obj->metadata;
|
|
|
|
fprintf(stderr, "%s", comma);
|
|
comma = ",";
|
|
|
|
if (!Metadata) {
|
|
fprintf(stderr, "{\"type\": \"null\"}");
|
|
continue;
|
|
}
|
|
|
|
const char *kindDescriptor = "";
|
|
switch (Metadata->getKind()) {
|
|
#define METADATAKIND(name, value) \
|
|
case MetadataKind::name: \
|
|
kindDescriptor = #name; \
|
|
break;
|
|
#include "swift/ABI/MetadataKind.def"
|
|
default:
|
|
kindDescriptor = "unknown";
|
|
break;
|
|
}
|
|
|
|
if (auto *NTD =
|
|
Metadata->getTypeContextDescriptor()) {
|
|
fprintf(stderr, "{"
|
|
"\"type\": \"nominal\", "
|
|
"\"name\": \"%s\", "
|
|
"\"kind\": \"%s\""
|
|
"}",
|
|
NTD->Name.get(), kindDescriptor);
|
|
continue;
|
|
}
|
|
|
|
fprintf(stderr, "{\"type\": \"unknown\", \"kind\": \"%s\"}",
|
|
kindDescriptor);
|
|
}
|
|
}
|
|
|
|
/// This assumes that the LeaksMutex is already being held.
|
|
static void dumpObjCHeapObjects() {
|
|
const char *comma = "";
|
|
for (id Obj : *TrackedObjCObjects) {
|
|
// Just print out the class of Obj.
|
|
fprintf(stderr, "%s\"%s\"", comma, object_getClassName(Obj));
|
|
comma = ",";
|
|
}
|
|
}
|
|
|
|
int _swift_leaks_stopTrackingObjects(const char *name) {
|
|
pthread_mutex_lock(&LeaksMutex);
|
|
unsigned Result = TrackedSwiftObjects->size() + TrackedObjCObjects->size();
|
|
|
|
fprintf(stderr, "{\"name\":\"%s\", \"swift_count\": %u, \"objc_count\": %u, "
|
|
"\"swift_objects\": [",
|
|
name, unsigned(TrackedSwiftObjects->size()),
|
|
unsigned(TrackedObjCObjects->size()));
|
|
dumpSwiftHeapObjects();
|
|
fprintf(stderr, "], \"objc_objects\": [");
|
|
dumpObjCHeapObjects();
|
|
fprintf(stderr, "]}\n");
|
|
|
|
fflush(stderr);
|
|
TrackedSwiftObjects->clear();
|
|
TrackedObjCObjects->clear();
|
|
ShouldTrackObjects = false;
|
|
|
|
// Undo our swizzling.
|
|
Method deallocMethod =
|
|
class_getInstanceMethod([NSObject class], @selector(dealloc));
|
|
Method allocMethod = class_getClassMethod([NSObject class], @selector(alloc));
|
|
Method allocWithZoneMethod =
|
|
class_getClassMethod([NSObject class], @selector(allocWithZone:));
|
|
|
|
method_setImplementation(deallocMethod, old_dealloc_fun);
|
|
method_setImplementation(allocMethod, old_alloc_fun);
|
|
method_setImplementation(allocWithZoneMethod, old_allocWithZone_fun);
|
|
|
|
pthread_mutex_unlock(&LeaksMutex);
|
|
return Result;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Tracking Code
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
void _swift_leaks_startTrackingObject(HeapObject *Object) {
|
|
pthread_mutex_lock(&LeaksMutex);
|
|
if (ShouldTrackObjects) {
|
|
TrackedSwiftObjects->insert(Object);
|
|
}
|
|
pthread_mutex_unlock(&LeaksMutex);
|
|
}
|
|
|
|
void _swift_leaks_stopTrackingObject(HeapObject *Object) {
|
|
pthread_mutex_lock(&LeaksMutex);
|
|
TrackedSwiftObjects->erase(Object);
|
|
pthread_mutex_unlock(&LeaksMutex);
|
|
}
|
|
|
|
static void _swift_leaks_startTrackingObjCObject(id Object) {
|
|
pthread_mutex_lock(&LeaksMutex);
|
|
if (ShouldTrackObjects) {
|
|
TrackedObjCObjects->insert(Object);
|
|
}
|
|
pthread_mutex_unlock(&LeaksMutex);
|
|
}
|
|
|
|
static void _swift_leaks_stopTrackingObjCObject(id Object) {
|
|
pthread_mutex_lock(&LeaksMutex);
|
|
TrackedObjCObjects->erase(Object);
|
|
pthread_mutex_unlock(&LeaksMutex);
|
|
}
|
|
|
|
#else
|
|
static char DummyDecl = '\0';
|
|
#endif
|