Showing preview only (278K chars total). Download the full file or copy to clipboard to get everything.
Repository: facebook/chisel
Branch: main
Commit: b28507a68daa
Files: 50
Total size: 262.5 KB
Directory structure:
gitextract_gmf84zuc/
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Chisel/
│ ├── Chisel/
│ │ ├── CHLAllocations.c
│ │ ├── CHLAllocations.h
│ │ ├── CHLObjcInstanceCommands.h
│ │ ├── CHLObjcInstanceCommands.mm
│ │ ├── CHLObjcInstances.h
│ │ ├── CHLObjcInstances.mm
│ │ ├── CHLPredicateTools.h
│ │ ├── CHLPredicateTools.m
│ │ ├── Chisel.h
│ │ ├── Info.plist
│ │ └── zone_allocator.h
│ ├── Chisel-macOS/
│ │ ├── Chisel_macOS.h
│ │ └── Info.plist
│ ├── Chisel.xcodeproj/
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace/
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata/
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ │ └── xcshareddata/
│ │ └── xcschemes/
│ │ └── Chisel.xcscheme
│ ├── ChiselTests/
│ │ ├── ChiselTests.m
│ │ └── Info.plist
│ └── Makefile
├── LICENSE
├── README.md
├── commands/
│ ├── FBAccessibilityCommands.py
│ ├── FBAutoLayoutCommands.py
│ ├── FBClassDump.py
│ ├── FBComponentCommands.py
│ ├── FBCopyCommands.py
│ ├── FBCounterCommands.py
│ ├── FBDebugCommands.py
│ ├── FBDelay.py
│ ├── FBDisplayCommands.py
│ ├── FBFindCommands.py
│ ├── FBFlickerCommands.py
│ ├── FBImportCommands.py
│ ├── FBInvocationCommands.py
│ ├── FBPrintCommands.py
│ ├── FBTextInputCommands.py
│ ├── FBVisualizationCommands.py
│ └── FBXCTestCommands.py
├── fbchisellldb.py
├── fbchisellldbbase.py
├── fbchisellldbinputhelpers.py
├── fbchisellldbobjcruntimehelpers.py
├── fbchisellldbobjecthelpers.py
├── fbchisellldbviewcontrollerhelpers.py
└── fbchisellldbviewhelpers.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
*.pyc
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Code of Conduct
Facebook has adopted a Code of Conduct that we expect project participants to adhere to. Please [read the full text](https://code.fb.com/codeofconduct) so that you can understand what actions will and will not be tolerated.
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Chisel
We want to make contributing to this project as easy and transparent as
possible.
## Pull Requests
We actively welcome your pull requests.
1. Fork the repo and create your branch from `master`.
2. If you've added code that should be tested, add tests
3. If you've changed APIs, update the documentation.
4. Ensure the test suite passes.
5. Make sure your code lints.
6. If you haven't already, complete the Contributor License Agreement ("CLA").
## Contributor License Agreement ("CLA")
In order to accept your pull request, we need you to submit a CLA. You only need
to do this once to work on any of Facebook's open source projects.
Complete your CLA here: <https://developers.facebook.com/opensource/cla>
## Issues
We use GitHub issues to track public bugs. Please ensure your description is
clear and has sufficient instructions to be able to reproduce the issue.
Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe
disclosure of security bugs. In those cases, please go through the process
outlined on that page and do not file a public issue.
## Updating Chisel in Brew (for maintainers)
Most users have Chisel installed via Homebrew. In order to update the version they'll receive when using `brew install` or `brew update`, we have to make some manual changes.
1. Create a new release in the GitHub web interface.
2. Download the `tar.gz` for that release and run `shasum -a 256 <path>`.
3. Copy the URL for the `.tar.gz` on the release page.
Run:
```
brew bump-formula-pr --strict chisel \
--url=<GitHub .tar.gz URL> \
--sha256=<output of shasum>
```
More docs on the process are available on the [Homebrew site](https://docs.brew.sh/How-To-Open-a-Homebrew-Pull-Request).
Example PRs:
- [Bump to 2.0.1](https://github.com/Homebrew/homebrew-core/pull/59799)
- [Bump to 2.0.0](https://github.com/Homebrew/homebrew-core/pull/50571)
## License
By contributing to Chisel, you agree that your contributions will be licensed
under its MIT license.
================================================
FILE: Chisel/Chisel/CHLAllocations.c
================================================
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
#include "CHLAllocations.h"
static kern_return_t reader(__unused task_t remote_task, vm_address_t remote_address, __unused vm_size_t size, void **local_memory)
{
*local_memory = (void *)remote_address;
return KERN_SUCCESS;
}
typedef struct {
CHLRangeHandler handler;
void *context;
} RangeEnumeratorArgs;
static void rangeEnumerator(__unused task_t task, void *context, __unused unsigned type, vm_range_t *ranges, unsigned int count)
{
const RangeEnumeratorArgs *args = (RangeEnumeratorArgs *)context;
for (unsigned int i = 0; i < count; ++i) {
args->handler(ranges[i], args->context);
}
}
void CHLScanAllocations(CHLRangeHandler handler, void *context, const malloc_zone_t *sideZone)
{
vm_address_t *zones;
unsigned int count;
malloc_get_all_zones(TASK_NULL, &reader, &zones, &count);
RangeEnumeratorArgs args = {handler, context};
for (unsigned int i = 0; i < count; ++i) {
malloc_zone_t *zone = (malloc_zone_t *)zones[i];
if (zone != sideZone) {
zone->introspect->enumerator(TASK_NULL, &args, MALLOC_PTR_IN_USE_RANGE_TYPE, (vm_address_t)zone, reader, rangeEnumerator);
}
}
}
================================================
FILE: Chisel/Chisel/CHLAllocations.h
================================================
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
#include <malloc/malloc.h>
#if defined(__cplusplus)
extern "C" {
#endif
typedef void (*CHLRangeHandler)(vm_range_t range, void *context);
// Enumerate live allocations in all malloc zones. If callers allocate memory in the handler, those
// allocations should be within the given `sideZone`.
void CHLScanAllocations(CHLRangeHandler handler, void *context, const malloc_zone_t *sideZone);
#if defined(__cplusplus)
}
#endif
================================================
FILE: Chisel/Chisel/CHLObjcInstanceCommands.h
================================================
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
@class NSPredicate;
#if defined(__cplusplus)
extern "C" {
#endif
// Debugger interface for finding and printing instances of a type, with an optional predicate.
// The predicate format is anything supported by NSPredicate.
void PrintInstances(const char *type, const char *pred);
#if defined(__cplusplus)
}
#endif
================================================
FILE: Chisel/Chisel/CHLObjcInstanceCommands.mm
================================================
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
#import "CHLObjcInstanceCommands.h"
#include <objc/runtime.h>
#include <vector>
#import <CoreFoundation/CoreFoundation.h>
#import <Foundation/Foundation.h>
#import "CHLObjcInstances.h"
#import "CHLPredicateTools.h"
#include "zone_allocator.h"
#if __has_feature(objc_arc)
#error Disable ARC for this file
#endif
struct IsValidArgs {
const std::unordered_set<Class> &classSet;
bool isValid = true;
};
static void isValidObject(const void *value, void *context)
{
const auto args = reinterpret_cast<IsValidArgs *>(context);
if (!args->isValid) {
return;
}
vm_range_t range = {(vm_address_t)value, malloc_size(value)};
if (CHLViableObjcInstance(range, args->classSet) == nil) {
args->isValid = false;
}
}
static void isValidKeyValue(const void *key, const void *value, void *context)
{
const auto args = reinterpret_cast<IsValidArgs *>(context);
isValidObject(key, context);
if (args->isValid) {
isValidObject(value, context);
}
}
static bool predicatePrecheck(id obj, const std::unordered_set<Class> &classSet)
{
IsValidArgs args{classSet};
if ([obj isKindOfClass:objc_getClass("__NSCFDictionary")]) {
CFDictionaryApplyFunction((CFDictionaryRef)obj, &isValidKeyValue, &args);
} else if ([obj isKindOfClass:objc_getClass("__NSCFSet")]) {
CFSetApplyFunction((CFSetRef)obj, &isValidObject, &args);
} else {
// Skip classes containing NSPlaceholder.
// TODO: Figure out better way to ignore invalid instances.
char *name = (char *)object_getClassName(obj);
while (*name == '_') ++name;
if (strncmp(name, "NSPlaceholder", sizeof("NSPlaceholder") - 1) == 0) {
args.isValid = false;
}
}
if (!args.isValid && getenv("FINDINSTANCES_DEBUG")) {
printf("%p has class %s but contains non objc data\n", obj, object_getClassName(obj));
}
return args.isValid;
}
static void printObject(id obj, NSSet *keyPaths) {
printf("<%s: %p", object_getClassName(obj), obj);
for (NSString *keyPath in keyPaths) {
printf("; %s = %s", keyPath.UTF8String, [[obj valueForKeyPath:keyPath] description].UTF8String);
}
printf(">\n");
}
static bool objectIsMatch(NSPredicate *predicate, id obj, const std::unordered_set<Class> &classSet)
{
if (!predicate) {
return true;
}
bool debug = getenv("FINDINSTANCES_DEBUG");
if (!predicatePrecheck(obj, classSet)) {
if (debug) {
printf("%p has class %s but has non objc contents\n", obj, object_getClassName(obj));
}
return false;
}
@try {
return [predicate evaluateWithObject:obj];
} @catch (...) {
if (debug) {
printf("%p has class %s but failed predicate evaluation\n", obj, object_getClassName(obj));
}
return false;
}
}
// Function reimplementation of +[NSObject isSubclassOf:] to avoid the objc runtime side
// effects that can happen when calling methods, like realizing classes, +initialize, etc.
static bool isSubclassOfClass(Class self, Class target)
{
for (auto cls = self; cls != Nil; cls = class_getSuperclass(cls)) {
if (cls == target) {
return true;
}
}
return false;
}
// Function reimplementation of +[NSObject conformsToProtocol:] to avoid the objc runtime side
// effects that can happen when calling methods, like realizing classes, +initialize, etc.
static bool conformsToProtocol(Class self, Protocol *protocol)
{
for (auto cls = self; cls != Nil; cls = class_getSuperclass(cls)) {
if (class_conformsToProtocol(cls, protocol)) {
return true;
}
}
return false;
}
void PrintInstances(const char *type, const char *pred)
{
NSPredicate *predicate = nil;
if (pred != nullptr && *pred != '\0') {
@try {
predicate = [NSPredicate predicateWithFormat:@(pred)];
} @catch (NSException *e) {
printf("Error: Invalid predicate; %s\n", [e reason].UTF8String);
return;
}
}
const std::unordered_set<Class> objcClasses = CHLObjcClassSet();
std::unordered_set<Class> matchClasses;
Protocol *protocol = objc_getProtocol(type);
if (protocol != nullptr && strcmp("NSObject", type) != 0) {
for (auto cls : objcClasses) {
if (conformsToProtocol(cls, protocol)) {
matchClasses.insert(cls);
}
}
}
bool exactClass = false;
if (type[0] == '*') {
exactClass = true;
++type;
}
// Helper lambda that only exists so that it can be called more than once, as in the
// rare case where `type` corresponds to more than one Swift class.
auto addMatch = [&](Class baseClass) {
if (exactClass) {
matchClasses.insert(baseClass);
} else {
for (auto cls : objcClasses) {
if (isSubclassOfClass(cls, baseClass)) {
matchClasses.insert(cls);
}
}
}
};
Class baseClass = objc_getClass(type);
if (baseClass != Nil) {
addMatch(baseClass);
} else {
// The given class name hasn't been found, this could be a Swift class which has
// a module name prefix. Loop over all classes to look for matching class names.
for (auto cls : objcClasses) {
// SwiftModule.ClassName
// ^- dot + 1
auto dot = strchr(class_getName(cls), '.');
if (dot && strcmp(type, dot + 1) == 0) {
addMatch(cls);
}
}
}
if (matchClasses.empty()) {
// TODO: Accept name of library/module, and list instances of classes defined there.
printf("Unknown type: %s\n", type);
return;
}
NSSet *keyPaths = CHLVariableKeyPaths(predicate);
auto instances = CHLScanObjcInstances(matchClasses);
unsigned int matches = 0;
for (id obj : instances) {
if (objectIsMatch(predicate, obj, objcClasses)) {
++matches;
printObject(obj, keyPaths);
}
}
if (matches > 1) {
printf("%d matches\n", matches);
}
}
================================================
FILE: Chisel/Chisel/CHLObjcInstances.h
================================================
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
#if defined(__cplusplus)
#include <malloc/malloc.h>
#include <unordered_set>
#include <vector>
#include "zone_allocator.h"
// Create a set containing all known Classes.
std::unordered_set<Class> CHLObjcClassSet();
// Enumerates the heap and returns all objects that appear to be legitimate.
std::vector<id, zone_allocator<id>> CHLScanObjcInstances(const std::unordered_set<Class> &classSet);
// Performs a number of heuristic checks on the memory range, to determine if the memory appears to
// be a viable Objective-C object.
id CHLViableObjcInstance(vm_range_t range, const std::unordered_set<Class> &classSet);
#endif
================================================
FILE: Chisel/Chisel/CHLObjcInstances.mm
================================================
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
#import "CHLObjcInstances.h"
#import <Foundation/Foundation.h>
#include "CHLAllocations.h"
#include <dlfcn.h>
#include <objc/message.h>
#include <objc/runtime.h>
#include <mach-o/getsect.h>
#if !defined(__LP64__)
using mach_header_t = mach_header;
#else
using mach_header_t = mach_header_64;
#endif
#include <unordered_set>
#include <vector>
#if __has_feature(objc_arc)
#error Disable ARC for this file
#endif
// Informal protocol to make it easy to call -_isDeallocating
@interface NSObject (Private)
- (BOOL)_isDeallocating;
@end
static id embeddedObjcInstance(vm_range_t range) {
Dl_info info;
bool aligned = range.address % alignof(void *) == 0;
uint8_t *pointer = (uint8_t *)range.address;
if (aligned && dladdr(pointer, &info)) {
unsigned long size = 0;
uint8_t *start = getsectiondata((mach_header_t *)info.dli_fbase, SEG_DATA, "__cfstring", &size);
uint8_t *end = start + size;
if (start <= pointer || pointer < end) {
// Found NSString/CFString constant.
return reinterpret_cast<id>(range.address);
}
}
return nil;
}
// TODO: Should this cache results instead of repeated lookups.
bool isFixedSizeClass(Class cls) {
const auto meta = object_getClass(cls);
const auto root = objc_getMetaClass("NSObject");
SEL allocs[] = { @selector(allocWithZone:), @selector(alloc) };
for (const auto &sel : allocs) {
IMP imp = class_getMethodImplementation(meta, sel);
if (imp != class_getMethodImplementation(root, sel)) {
// Class overrides NSObject alloc method, may not have fixed sizes.
return false;
}
}
return true;
}
// Runs a number of heuristics on the given address. Returns nil if any heuristic fails, otherwise
// returns that address casted as an object.
//
// Currently the heuristics don't fully guarantee that the returned object is an actual object, but
// when using MallocScribble=1, false positives are unlikely. Further, callers will generally do
// higher level filtering, for example checking a value on the object, which can further eliminate
// false positives.
//
// There's also one known false negative case, NSConcreteValue can store data in the malloc memory
// beyond its instance size. Currently this false negative is allowed.
id CHLViableObjcInstance(vm_range_t range, const std::unordered_set<Class> &classSet)
{
// Check if this address points to an object embedded into Mach-O.
if (range.size == 0) {
return embeddedObjcInstance(range);
}
id obj = reinterpret_cast<id>(range.address);
// It's safe to call object_getClass on memory that isn't objc objects.
// Check that the returned Class points to an expected class.
Class cls = object_getClass(obj);
if (classSet.find(cls) == classSet.end()) {
return nil;
}
// Instance size is the byte count needed for an object's ivars, plus any padding.
// Allocation size is the byte count that malloc will actually allocate for instances of a Class.
const auto instanceSize = class_getInstanceSize(cls);
const auto expectedAllocationSize = malloc_good_size(instanceSize);
const auto extraSize = expectedAllocationSize - instanceSize;
const bool debug = getenv("FINDINSTANCES_DEBUG") != NULL;
if (range.size < expectedAllocationSize) {
if (debug) {
printf("%p has class %s but is too small\n", obj, class_getName(cls));
}
return nil;
}
if (range.size > expectedAllocationSize && isFixedSizeClass(cls)) {
// Range is too big and the class has no way of allocating larger instances.
if (debug) {
printf("%p has fixed size class %s but is too large\n", obj, class_getName(cls));
}
return nil;
}
if (range.size == expectedAllocationSize && extraSize) {
// ObjC instances are allocated with calloc, memory beyond the instance size should be zeros.
// Some classes have been known to store data in the extra space, ex NSConcreteValue.
static const unsigned char ZEROS[1024] = {0};
auto extra = object_getIndexedIvars(obj);
auto compareSize = std::min(extraSize, sizeof(ZEROS));
if (memcmp(extra, &ZEROS, compareSize) != 0) {
if (debug) {
printf("%p has class %s but has non-zero memory\n", obj, class_getName(cls));
}
return nil;
}
}
// Ignore deallocating objects.
if ([obj _isDeallocating]) {
return nil;
}
return obj;
}
struct FindViableObjcInstancesArgs {
const std::unordered_set<Class> &classSet;
std::vector<id, zone_allocator<id>> &instances;
};
static void findViableObjcInstances(vm_range_t range, void *context)
{
const auto args = reinterpret_cast<FindViableObjcInstancesArgs *>(context);
id obj = CHLViableObjcInstance(range, args->classSet);
if (obj != nil) {
args->instances.push_back(obj);
}
}
std::vector<id, zone_allocator<id>> CHLScanObjcInstances(const std::unordered_set<Class> &classSet)
{
std::vector<id, zone_allocator<id>> instances;
FindViableObjcInstancesArgs args{classSet, instances};
CHLScanAllocations(&findViableObjcInstances, &args, instances.get_allocator().zone());
return instances;
}
std::unordered_set<Class> CHLObjcClassSet()
{
unsigned int count = 0;
auto classList = objc_copyClassList(&count);
std::unordered_set<Class> classSet{classList, classList + count, count};
free(classList);
return classSet;
}
================================================
FILE: Chisel/Chisel/CHLPredicateTools.h
================================================
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
#import <Foundation/Foundation.h>
#if defined(__cplusplus)
extern "C" {
#endif
NSSet *CHLVariableKeyPaths(NSPredicate *predicate);
#if defined(__cplusplus)
}
#endif
================================================
FILE: Chisel/Chisel/CHLPredicateTools.m
================================================
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
#import "CHLPredicateTools.h"
static bool isEqualToConstantComparison(NSComparisonPredicate *predicate)
{
bool equality = predicate.predicateOperatorType == NSEqualToPredicateOperatorType;
bool direct = predicate.comparisonPredicateModifier == NSDirectPredicateModifier;
bool constantLeft = predicate.leftExpression.expressionType == NSConstantValueExpressionType;
bool constantRight = predicate.rightExpression.expressionType == NSConstantValueExpressionType;
return equality && direct && (constantLeft || constantRight);
}
NSSet *CHLVariableKeyPaths(NSPredicate *predicate)
{
if (predicate == nil) {
return nil;
}
NSMutableSet *keyPaths = [NSMutableSet new];
NSMutableArray *predicateStack = [NSMutableArray arrayWithObject:predicate];
while (predicateStack.count > 0) {
NSPredicate *subpredicate = [predicateStack lastObject];
[predicateStack removeLastObject];
if ([subpredicate isKindOfClass:[NSCompoundPredicate class]]) {
NSCompoundPredicate *compoundPredicate = (NSCompoundPredicate *)subpredicate;
[predicateStack addObjectsFromArray:compoundPredicate.subpredicates];
continue;
}
if ([subpredicate isKindOfClass:[NSComparisonPredicate class]]) {
NSComparisonPredicate *comparisonPredicate = (NSComparisonPredicate *)subpredicate;
if (isEqualToConstantComparison(comparisonPredicate)) {
// Keypaths equal to constants are not variable. Skip these to not be noisy.
// ex `username == "jonalan"` or `alpha == 0`
continue;
}
// TODO: Handle NSFunctionExpressionType
if (comparisonPredicate.leftExpression.expressionType == NSKeyPathExpressionType) {
[keyPaths addObject:comparisonPredicate.leftExpression.keyPath];
}
if (comparisonPredicate.rightExpression.expressionType == NSKeyPathExpressionType) {
[keyPaths addObject:comparisonPredicate.rightExpression.keyPath];
}
}
}
return keyPaths;
}
================================================
FILE: Chisel/Chisel/Chisel.h
================================================
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
#import <UIKit/UIKit.h>
//! Project version number for Chisel.
FOUNDATION_EXPORT double ChiselVersionNumber;
//! Project version string for Chisel.
FOUNDATION_EXPORT const unsigned char ChiselVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <Chisel/PublicHeader.h>
================================================
FILE: Chisel/Chisel/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
================================================
FILE: Chisel/Chisel/zone_allocator.h
================================================
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
#pragma once
#include <memory>
#include <malloc/malloc.h>
template <typename T>
struct zone_allocator {
using value_type = T;
T *allocate(std::size_t n)
{
auto allocation = malloc_zone_malloc(_zone.get(), n * sizeof(T));
return reinterpret_cast<T *>(allocation);
}
void deallocate(T *p, __unused std::size_t n)
{
malloc_zone_free(_zone.get(), p);
}
const malloc_zone_t *zone() const
{
return _zone.get();
}
private:
std::shared_ptr<malloc_zone_t> _zone{malloc_create_zone(0x200, 0), &malloc_destroy_zone};
};
template <typename T, typename U>
bool operator==(const zone_allocator<T> &a, const zone_allocator<T> &b) noexcept
{
return a.zone() == b.zone();
}
template <typename T, typename U>
bool operator!=(const zone_allocator<T> &a, const zone_allocator<T> &b) noexcept
{
return !(a == b);
}
================================================
FILE: Chisel/Chisel-macOS/Chisel_macOS.h
================================================
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
#import <Cocoa/Cocoa.h>
//! Project version number for Chisel_macOS.
FOUNDATION_EXPORT double Chisel_macOSVersionNumber;
//! Project version string for Chisel_macOS.
FOUNDATION_EXPORT const unsigned char Chisel_macOSVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <Chisel_macOS/PublicHeader.h>
================================================
FILE: Chisel/Chisel-macOS/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
================================================
FILE: Chisel/Chisel.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
70D1EACB20FD9D4A004CDB3D /* Chisel_macOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 70D1EAC920FD9D4A004CDB3D /* Chisel_macOS.h */; settings = {ATTRIBUTES = (Public, ); }; };
70D1EACF20FD9D82004CDB3D /* CHLPredicateTools.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A20C4B21DFDB8D200C89959 /* CHLPredicateTools.m */; };
70D1EAD020FD9D85004CDB3D /* CHLObjcInstanceCommands.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7A04088B1DF9A2C7009C5BFA /* CHLObjcInstanceCommands.mm */; };
70D1EAD120FD9D88004CDB3D /* CHLObjcInstances.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7ABD17AA1DF7FCF9006118F8 /* CHLObjcInstances.mm */; };
70D1EAD220FD9D8C004CDB3D /* CHLAllocations.c in Sources */ = {isa = PBXBuildFile; fileRef = 7ABD17A61DF7F9FD006118F8 /* CHLAllocations.c */; };
7A04088C1DF9A2C7009C5BFA /* CHLObjcInstanceCommands.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A04088A1DF9A2C7009C5BFA /* CHLObjcInstanceCommands.h */; };
7A04088D1DF9A2C7009C5BFA /* CHLObjcInstanceCommands.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7A04088B1DF9A2C7009C5BFA /* CHLObjcInstanceCommands.mm */; };
7A20C4B31DFDB8D200C89959 /* CHLPredicateTools.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A20C4B11DFDB8D200C89959 /* CHLPredicateTools.h */; };
7A20C4B41DFDB8D200C89959 /* CHLPredicateTools.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A20C4B21DFDB8D200C89959 /* CHLPredicateTools.m */; };
7ABD17951DF7F998006118F8 /* Chisel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7ABD178B1DF7F998006118F8 /* Chisel.framework */; };
7ABD179A1DF7F998006118F8 /* ChiselTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ABD17991DF7F998006118F8 /* ChiselTests.m */; };
7ABD179C1DF7F998006118F8 /* Chisel.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ABD178E1DF7F998006118F8 /* Chisel.h */; settings = {ATTRIBUTES = (Public, ); }; };
7ABD17A71DF7F9FD006118F8 /* CHLAllocations.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ABD17A51DF7F9FD006118F8 /* CHLAllocations.h */; };
7ABD17A81DF7F9FD006118F8 /* CHLAllocations.c in Sources */ = {isa = PBXBuildFile; fileRef = 7ABD17A61DF7F9FD006118F8 /* CHLAllocations.c */; };
7ABD17AB1DF7FCF9006118F8 /* CHLObjcInstances.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ABD17A91DF7FCF9006118F8 /* CHLObjcInstances.h */; };
7ABD17AC1DF7FCF9006118F8 /* CHLObjcInstances.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7ABD17AA1DF7FCF9006118F8 /* CHLObjcInstances.mm */; };
7ABD17AF1DF88520006118F8 /* zone_allocator.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ABD17AD1DF88520006118F8 /* zone_allocator.h */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
7ABD17961DF7F998006118F8 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 7ABD17821DF7F998006118F8 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 7ABD178A1DF7F998006118F8;
remoteInfo = Chisel;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
70D1EAC720FD9D4A004CDB3D /* Chisel_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Chisel_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
70D1EAC920FD9D4A004CDB3D /* Chisel_macOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Chisel_macOS.h; sourceTree = "<group>"; };
70D1EACA20FD9D4A004CDB3D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
7A04088A1DF9A2C7009C5BFA /* CHLObjcInstanceCommands.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHLObjcInstanceCommands.h; sourceTree = "<group>"; };
7A04088B1DF9A2C7009C5BFA /* CHLObjcInstanceCommands.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CHLObjcInstanceCommands.mm; sourceTree = "<group>"; };
7A20C4B11DFDB8D200C89959 /* CHLPredicateTools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHLPredicateTools.h; sourceTree = "<group>"; };
7A20C4B21DFDB8D200C89959 /* CHLPredicateTools.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CHLPredicateTools.m; sourceTree = "<group>"; };
7ABD178B1DF7F998006118F8 /* Chisel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Chisel.framework; sourceTree = BUILT_PRODUCTS_DIR; };
7ABD178E1DF7F998006118F8 /* Chisel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Chisel.h; sourceTree = "<group>"; };
7ABD178F1DF7F998006118F8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
7ABD17941DF7F998006118F8 /* ChiselTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ChiselTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
7ABD17991DF7F998006118F8 /* ChiselTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ChiselTests.m; sourceTree = "<group>"; };
7ABD179B1DF7F998006118F8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
7ABD17A51DF7F9FD006118F8 /* CHLAllocations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHLAllocations.h; sourceTree = "<group>"; };
7ABD17A61DF7F9FD006118F8 /* CHLAllocations.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = CHLAllocations.c; sourceTree = "<group>"; };
7ABD17A91DF7FCF9006118F8 /* CHLObjcInstances.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHLObjcInstances.h; sourceTree = "<group>"; };
7ABD17AA1DF7FCF9006118F8 /* CHLObjcInstances.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CHLObjcInstances.mm; sourceTree = "<group>"; };
7ABD17AD1DF88520006118F8 /* zone_allocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = zone_allocator.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
70D1EAC320FD9D4A004CDB3D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
7ABD17871DF7F998006118F8 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
7ABD17911DF7F998006118F8 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
7ABD17951DF7F998006118F8 /* Chisel.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
70D1EAC820FD9D4A004CDB3D /* Chisel-macOS */ = {
isa = PBXGroup;
children = (
70D1EAC920FD9D4A004CDB3D /* Chisel_macOS.h */,
70D1EACA20FD9D4A004CDB3D /* Info.plist */,
);
path = "Chisel-macOS";
sourceTree = "<group>";
};
7ABD17811DF7F998006118F8 = {
isa = PBXGroup;
children = (
7ABD178D1DF7F998006118F8 /* Chisel */,
7ABD17981DF7F998006118F8 /* ChiselTests */,
70D1EAC820FD9D4A004CDB3D /* Chisel-macOS */,
7ABD178C1DF7F998006118F8 /* Products */,
);
indentWidth = 2;
sourceTree = "<group>";
};
7ABD178C1DF7F998006118F8 /* Products */ = {
isa = PBXGroup;
children = (
7ABD178B1DF7F998006118F8 /* Chisel.framework */,
7ABD17941DF7F998006118F8 /* ChiselTests.xctest */,
70D1EAC720FD9D4A004CDB3D /* Chisel_macOS.framework */,
);
name = Products;
sourceTree = "<group>";
};
7ABD178D1DF7F998006118F8 /* Chisel */ = {
isa = PBXGroup;
children = (
7ABD178E1DF7F998006118F8 /* Chisel.h */,
7ABD178F1DF7F998006118F8 /* Info.plist */,
7ABD17AD1DF88520006118F8 /* zone_allocator.h */,
7ABD17A51DF7F9FD006118F8 /* CHLAllocations.h */,
7ABD17A61DF7F9FD006118F8 /* CHLAllocations.c */,
7ABD17A91DF7FCF9006118F8 /* CHLObjcInstances.h */,
7ABD17AA1DF7FCF9006118F8 /* CHLObjcInstances.mm */,
7A04088A1DF9A2C7009C5BFA /* CHLObjcInstanceCommands.h */,
7A04088B1DF9A2C7009C5BFA /* CHLObjcInstanceCommands.mm */,
7A20C4B11DFDB8D200C89959 /* CHLPredicateTools.h */,
7A20C4B21DFDB8D200C89959 /* CHLPredicateTools.m */,
);
path = Chisel;
sourceTree = "<group>";
};
7ABD17981DF7F998006118F8 /* ChiselTests */ = {
isa = PBXGroup;
children = (
7ABD17991DF7F998006118F8 /* ChiselTests.m */,
7ABD179B1DF7F998006118F8 /* Info.plist */,
);
path = ChiselTests;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
70D1EAC420FD9D4A004CDB3D /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
70D1EACB20FD9D4A004CDB3D /* Chisel_macOS.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
7ABD17881DF7F998006118F8 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
7ABD17A71DF7F9FD006118F8 /* CHLAllocations.h in Headers */,
7ABD179C1DF7F998006118F8 /* Chisel.h in Headers */,
7A20C4B31DFDB8D200C89959 /* CHLPredicateTools.h in Headers */,
7A04088C1DF9A2C7009C5BFA /* CHLObjcInstanceCommands.h in Headers */,
7ABD17AF1DF88520006118F8 /* zone_allocator.h in Headers */,
7ABD17AB1DF7FCF9006118F8 /* CHLObjcInstances.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
70D1EAC620FD9D4A004CDB3D /* Chisel-macOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = 70D1EACE20FD9D4A004CDB3D /* Build configuration list for PBXNativeTarget "Chisel-macOS" */;
buildPhases = (
70D1EAC220FD9D4A004CDB3D /* Sources */,
70D1EAC320FD9D4A004CDB3D /* Frameworks */,
70D1EAC420FD9D4A004CDB3D /* Headers */,
70D1EAC520FD9D4A004CDB3D /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "Chisel-macOS";
productName = "Chisel-macOS";
productReference = 70D1EAC720FD9D4A004CDB3D /* Chisel_macOS.framework */;
productType = "com.apple.product-type.framework";
};
7ABD178A1DF7F998006118F8 /* Chisel */ = {
isa = PBXNativeTarget;
buildConfigurationList = 7ABD179F1DF7F998006118F8 /* Build configuration list for PBXNativeTarget "Chisel" */;
buildPhases = (
7ABD17861DF7F998006118F8 /* Sources */,
7ABD17871DF7F998006118F8 /* Frameworks */,
7ABD17881DF7F998006118F8 /* Headers */,
7ABD17891DF7F998006118F8 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = Chisel;
productName = Chisel;
productReference = 7ABD178B1DF7F998006118F8 /* Chisel.framework */;
productType = "com.apple.product-type.framework";
};
7ABD17931DF7F998006118F8 /* ChiselTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 7ABD17A21DF7F998006118F8 /* Build configuration list for PBXNativeTarget "ChiselTests" */;
buildPhases = (
7ABD17901DF7F998006118F8 /* Sources */,
7ABD17911DF7F998006118F8 /* Frameworks */,
7ABD17921DF7F998006118F8 /* Resources */,
);
buildRules = (
);
dependencies = (
7ABD17971DF7F998006118F8 /* PBXTargetDependency */,
);
name = ChiselTests;
productName = ChiselTests;
productReference = 7ABD17941DF7F998006118F8 /* ChiselTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
7ABD17821DF7F998006118F8 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0810;
ORGANIZATIONNAME = Facebook;
TargetAttributes = {
70D1EAC620FD9D4A004CDB3D = {
CreatedOnToolsVersion = 9.4.1;
ProvisioningStyle = Automatic;
};
7ABD178A1DF7F998006118F8 = {
CreatedOnToolsVersion = 8.1;
ProvisioningStyle = Automatic;
};
7ABD17931DF7F998006118F8 = {
CreatedOnToolsVersion = 8.1;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = 7ABD17851DF7F998006118F8 /* Build configuration list for PBXProject "Chisel" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 7ABD17811DF7F998006118F8;
productRefGroup = 7ABD178C1DF7F998006118F8 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
7ABD178A1DF7F998006118F8 /* Chisel */,
7ABD17931DF7F998006118F8 /* ChiselTests */,
70D1EAC620FD9D4A004CDB3D /* Chisel-macOS */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
70D1EAC520FD9D4A004CDB3D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
7ABD17891DF7F998006118F8 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
7ABD17921DF7F998006118F8 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
70D1EAC220FD9D4A004CDB3D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
70D1EAD120FD9D88004CDB3D /* CHLObjcInstances.mm in Sources */,
70D1EAD220FD9D8C004CDB3D /* CHLAllocations.c in Sources */,
70D1EAD020FD9D85004CDB3D /* CHLObjcInstanceCommands.mm in Sources */,
70D1EACF20FD9D82004CDB3D /* CHLPredicateTools.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
7ABD17861DF7F998006118F8 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
7ABD17A81DF7F9FD006118F8 /* CHLAllocations.c in Sources */,
7ABD17AC1DF7FCF9006118F8 /* CHLObjcInstances.mm in Sources */,
7A04088D1DF9A2C7009C5BFA /* CHLObjcInstanceCommands.mm in Sources */,
7A20C4B41DFDB8D200C89959 /* CHLPredicateTools.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
7ABD17901DF7F998006118F8 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
7ABD179A1DF7F998006118F8 /* ChiselTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
7ABD17971DF7F998006118F8 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 7ABD178A1DF7F998006118F8 /* Chisel */;
targetProxy = 7ABD17961DF7F998006118F8 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
70D1EACC20FD9D4A004CDB3D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_VERSION = A;
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = "Chisel-macOS/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.13;
PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.Chisel-macOS";
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SDKROOT = macosx;
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 4.0;
};
name = Debug;
};
70D1EACD20FD9D4A004CDB3D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_VERSION = A;
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = "Chisel-macOS/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.13;
PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.Chisel-macOS";
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SDKROOT = macosx;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 4.0;
};
name = Release;
};
7ABD179D1DF7F998006118F8 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "c++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = NO;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = c11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 10.1;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
7ABD179E1DF7F998006118F8 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "c++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = NO;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = c11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 10.1;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
7ABD17A01DF7F998006118F8 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = Chisel/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.Chisel;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = NO;
};
name = Debug;
};
7ABD17A11DF7F998006118F8 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = Chisel/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.Chisel;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = NO;
};
name = Release;
};
7ABD17A31DF7F998006118F8 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
INFOPLIST_FILE = ChiselTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.ChiselTests;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
7ABD17A41DF7F998006118F8 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
INFOPLIST_FILE = ChiselTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.ChiselTests;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
70D1EACE20FD9D4A004CDB3D /* Build configuration list for PBXNativeTarget "Chisel-macOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
70D1EACC20FD9D4A004CDB3D /* Debug */,
70D1EACD20FD9D4A004CDB3D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
7ABD17851DF7F998006118F8 /* Build configuration list for PBXProject "Chisel" */ = {
isa = XCConfigurationList;
buildConfigurations = (
7ABD179D1DF7F998006118F8 /* Debug */,
7ABD179E1DF7F998006118F8 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
7ABD179F1DF7F998006118F8 /* Build configuration list for PBXNativeTarget "Chisel" */ = {
isa = XCConfigurationList;
buildConfigurations = (
7ABD17A01DF7F998006118F8 /* Debug */,
7ABD17A11DF7F998006118F8 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
7ABD17A21DF7F998006118F8 /* Build configuration list for PBXNativeTarget "ChiselTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
7ABD17A31DF7F998006118F8 /* Debug */,
7ABD17A41DF7F998006118F8 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 7ABD17821DF7F998006118F8 /* Project object */;
}
================================================
FILE: Chisel/Chisel.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:Chisel.xcodeproj">
</FileRef>
</Workspace>
================================================
FILE: Chisel/Chisel.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
================================================
FILE: Chisel/Chisel.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildSystemType</key>
<string>Original</string>
</dict>
</plist>
================================================
FILE: Chisel/Chisel.xcodeproj/xcshareddata/xcschemes/Chisel.xcscheme
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0920"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7ABD178A1DF7F998006118F8"
BuildableName = "Chisel.framework"
BlueprintName = "Chisel"
ReferencedContainer = "container:Chisel.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7ABD17931DF7F998006118F8"
BuildableName = "ChiselTests.xctest"
BlueprintName = "ChiselTests"
ReferencedContainer = "container:Chisel.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7ABD178A1DF7F998006118F8"
BuildableName = "Chisel.framework"
BlueprintName = "Chisel"
ReferencedContainer = "container:Chisel.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7ABD178A1DF7F998006118F8"
BuildableName = "Chisel.framework"
BlueprintName = "Chisel"
ReferencedContainer = "container:Chisel.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7ABD178A1DF7F998006118F8"
BuildableName = "Chisel.framework"
BlueprintName = "Chisel"
ReferencedContainer = "container:Chisel.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
================================================
FILE: Chisel/ChiselTests/ChiselTests.m
================================================
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
#import <XCTest/XCTest.h>
@interface ChiselTests : XCTestCase
@end
@implementation ChiselTests
- (void)setUp {
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown {
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testExample {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
- (void)testPerformanceExample {
// This is an example of a performance test case.
[self measureBlock:^{
// Put the code you want to measure the time of here.
}];
}
@end
================================================
FILE: Chisel/ChiselTests/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
================================================
FILE: Chisel/Makefile
================================================
PREFIX ?= /usr/local/lib
export INSTALL_NAME =
ifneq ($(LD_DYLIB_INSTALL_NAME),)
INSTALL_NAME = "LD_DYLIB_INSTALL_NAME=$(LD_DYLIB_INSTALL_NAME)"
endif
install:
xcodebuild \
-scheme Chisel \
-configuration Release \
-sdk iphonesimulator \
install \
$(INSTALL_NAME) \
DSTROOT=/ \
INSTALL_PATH="$(PREFIX)"
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) Facebook, Inc. and its affiliates.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# Chisel
`Chisel` is a collection of `LLDB` commands to assist in the debugging of iOS apps.
[[Installation](#installation) • [Commands](#commands) • [Custom Commands](#custom-commands) • [Development Workflow](#development-workflow) [Contributing](#contributing) • [License](#license)]
For a comprehensive overview of LLDB, and how Chisel complements it, read Ari Grant's [Dancing in the Debugger — A Waltz with LLDB](http://www.objc.io/issue-19/lldb-debugging.html) in issue 19 of [objc.io](http://www.objc.io/).
## Installation
```shell
brew update
brew install chisel
```
if `.lldbinit` file doesn't exist you can create it & open it by tapping on the terminal
```shell
touch .lldbinit
open .lldbinit
```
Then add the following line to your `~/.lldbinit` file.
```Python
# ~/.lldbinit
...
command script import /usr/local/opt/chisel/libexec/fbchisellldb.py
```
* Note that if you are installing on an M1 Mac, the path above should be `/opt/homebrew/opt/chisel/libexec/fbchisellldb.py` instead.
Alternatively, download chisel and add the following line to your _~/.lldbinit_ file.
```Python
# ~/.lldbinit
...
command script import /path/to/fbchisellldb.py
```
The commands will be available the next time `Xcode` starts.
## Commands
There are many commands; here's a few:
*(Compatibility with iOS/Mac indicated at right)*
|Command |Description |iOS |OS X |
|-----------------|----------------|-------|-------|
|pviews |Print the recursive view description for the key window.|Yes|Yes|
|pvc |Print the recursive view controller description for the key window.|Yes|No|
|visualize |Open a `UIImage`, `CGImageRef`, `UIView`, `CALayer`, `NSData` (of an image), `UIColor`, `CIColor`, `CIImage`, `CGColorRef` or `CVPixelBuffer` in Preview.app on your Mac.|Yes|No|
|fv |Find a view in the hierarchy whose class name matches the provided regex.|Yes|No|
|fvc |Find a view controller in the hierarchy whose class name matches the provided regex.|Yes|No|
|show/hide |Show or hide the given view or layer. You don't even have to continue the process to see the changes!|Yes|Yes|
|mask/unmask |Overlay a view or layer with a transparent rectangle to visualize where it is.|Yes|No|
|border/unborder |Add a border to a view or layer to visualize where it is.|Yes|Yes|
|caflush |Flush the render server (equivalent to a "repaint" if no animations are in-flight).|Yes|Yes|
|bmessage |Set a symbolic breakpoint on the method of a class or the method of an instance without worrying which class in the hierarchy actually implements the method.|Yes|Yes|
|wivar |Set a watchpoint on an instance variable of an object.|Yes|Yes|
|presponder |Print the responder chain starting from the given object.|Yes|Yes|
|... |... and many more!|
To see the list of **all** of the commands execute the help command in `LLDB` or go to the [Wiki](https://github.com/facebook/chisel/wiki).
```Python
(lldb) help
The following is a list of built-in, permanent debugger commands:
...
The following is a list of your current user-defined commands:
...
```
The bottom list contains all the commands sourced from `Chisel`.
You can also inspect a specific command by passing its name as an argument to the help command (as with all other `LLDB` commands).
```
(lldb) help border
Draws a border around <viewOrLayer>. Color and width can be optionally provided.
Arguments:
<viewOrLayer>; Type: UIView*; The view to border.
Options:
--color/-c <color>; Type: string; A color name such as 'red', 'green', 'magenta', etc.
--width/-w <width>; Type: CGFloat; Desired width of border.
Syntax: border [--color=color] [--width=width] <viewOrLayer>
```
All of the commands provided by `Chisel` come with verbose help. Be sure to read it when in doubt!
## Custom Commands
You can add local, custom commands. Here's a contrived example.
```python
#!/usr/bin/python
# Example file with custom commands, located at /magical/commands/example.py
import lldb
import fbchisellldbbase as fb
def lldbcommands():
return [ PrintKeyWindowLevel() ]
class PrintKeyWindowLevel(fb.FBCommand):
def name(self):
return 'pkeywinlevel'
def description(self):
return 'An incredibly contrived command that prints the window level of the key window.'
def run(self, arguments, options):
# It's a good habit to explicitly cast the type of all return
# values and arguments. LLDB can't always find them on its own.
lldb.debugger.HandleCommand('p (CGFloat)[(id)[(id)[UIApplication sharedApplication] keyWindow] windowLevel]')
```
Then all that's left is to source the commands in lldbinit. `Chisel` has a python function just for this, _loadCommandsInDirectory_ in the _fbobjclldb.py_ module.
```Python
# ~/.lldbinit
...
command script import /path/to/fbobjclldb.py
script fbobjclldb.loadCommandsInDirectory('/magical/commands/')
```
There's also builtin support to make it super easy to specify the arguments and options that a command takes. See the _border_ and _pinvocation_ commands for example use.
## Development Workflow
Developing commands, whether for local use or contributing to `Chisel` directly, both follow the same workflow. Create a command as described in the [Custom Commands](#custom-commands) section and then
1. Start `LLDB`
2. Reach a breakpoint (or simply pause execution via the pause button in `Xcode`'s debug bar or `process interrupt` if attached directly)
3. Execute `command source ~/.lldbinit` in LLDB to source the commands
4. Run the command you are working on
5. Modify the command
6. Optionally run `script reload(modulename)`
7. Repeat steps 3-6 until the command becomes a source of happiness
## Contributing
Please contribute any generic commands that you make. If it helps you then it will likely help many others! :D See `CONTRIBUTING.md` to learn how to contribute.
## License
`Chisel` is MIT-licensed. See `LICENSE`.
================================================
FILE: commands/FBAccessibilityCommands.py
================================================
#!/usr/bin/python
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
import os
import re
import fbchisellldbbase as fb
import fbchisellldbobjecthelpers as objHelpers
# This is the key corresponding to accessibility label in
# _accessibilityElementsInContainer:
ACCESSIBILITY_LABEL_KEY = 2001
def lldbcommands():
return [
FBPrintAccessibilityLabels(),
FBPrintAccessibilityIdentifiers(),
FBFindViewByAccessibilityLabelCommand(),
]
class FBPrintAccessibilityLabels(fb.FBCommand):
def name(self):
return "pa11y"
def description(self):
return "Print accessibility labels of all views in hierarchy of <aView>"
def args(self):
return [
fb.FBCommandArgument(
arg="aView",
type="UIView*",
help="The view to print the hierarchy of.",
default="(id)[[UIApplication sharedApplication] keyWindow]",
)
]
def run(self, arguments, options):
forceStartAccessibilityServer()
printAccessibilityHierarchy(arguments[0])
class FBPrintAccessibilityIdentifiers(fb.FBCommand):
def name(self):
return "pa11yi"
def description(self):
return "Print accessibility identifiers of all views in hierarchy of <aView>"
def args(self):
return [
fb.FBCommandArgument(
arg="aView",
type="UIView*",
help="The view to print the hierarchy of.",
default="(id)[[UIApplication sharedApplication] keyWindow]",
)
]
def run(self, arguments, option):
forceStartAccessibilityServer()
printAccessibilityIdentifiersHierarchy(arguments[0])
class FBFindViewByAccessibilityLabelCommand(fb.FBCommand):
def name(self):
return "fa11y"
def description(self):
return (
"Find the views whose accessibility labels match labelRegex "
"and puts the address of the first result on the clipboard."
)
def args(self):
return [
fb.FBCommandArgument(
arg="labelRegex",
type="string",
help="The accessibility label regex to search the view hierarchy for.",
)
]
def accessibilityGrepHierarchy(self, view, needle):
a11yLabel = accessibilityLabel(view)
# if we don't have any accessibility string - we should have some children
if int(a11yLabel.GetValue(), 16) == 0:
# We call private method that gives back all visible accessibility children
# for view iOS 10 and higher
if fb.evaluateBooleanExpression(
"[UIView respondsToSelector:@selector(_accessibilityElementsAndContainersDescendingFromViews:options:sorted:)]"
):
accessibilityElements = fb.evaluateObjectExpression(
"[UIView _accessibilityElementsAndContainersDescendingFromViews:@[(id)%s] options:0 sorted:NO]"
% view
)
else:
accessibilityElements = fb.evaluateObjectExpression(
"[[[UIApplication sharedApplication] keyWindow] _accessibilityElementsInContainer:0 topLevel:%s includeKB:0]"
% view
)
accessibilityElementsCount = fb.evaluateIntegerExpression(
"[%s count]" % accessibilityElements
)
for index in range(0, accessibilityElementsCount):
subview = fb.evaluateObjectExpression(
"[%s objectAtIndex:%i]" % (accessibilityElements, index)
)
self.accessibilityGrepHierarchy(subview, needle)
elif re.match(
r".*" + needle + ".*", a11yLabel.GetObjectDescription(), re.IGNORECASE
):
classDesc = objHelpers.className(view)
print(
"({} {}) {}".format(classDesc, view, a11yLabel.GetObjectDescription())
)
# First element that is found is copied to clipboard
if not self.foundElement:
self.foundElement = True
cmd = 'echo %s | tr -d "\n" | pbcopy' % view
os.system(cmd)
def run(self, arguments, options):
forceStartAccessibilityServer()
rootView = fb.evaluateObjectExpression(
"[[UIApplication sharedApplication] keyWindow]"
)
self.foundElement = False
self.accessibilityGrepHierarchy(rootView, arguments[0])
def isRunningInSimulator():
return (
fb.evaluateExpressionValue("(id)[[UIDevice currentDevice] model]")
.GetObjectDescription()
.lower()
.find("simulator")
>= 0
) or (
fb.evaluateExpressionValue("(id)[[UIDevice currentDevice] name]")
.GetObjectDescription()
.lower()
.find("simulator")
>= 0
)
def forceStartAccessibilityServer():
# We try to start accessibility server only if we don't have needed method active
if not fb.evaluateBooleanExpression(
"[UIView instancesRespondToSelector:@selector(_accessibilityElementsInContainer:)]"
):
# Starting accessibility server is different for simulator and device
if isRunningInSimulator():
fb.evaluateEffect(
"[[UIApplication sharedApplication] accessibilityActivate]"
)
else:
fb.evaluateEffect(
"[[[UIApplication sharedApplication] _accessibilityBundlePrincipalClass] _accessibilityStartServer]"
)
def accessibilityLabel(view):
# using Apple private API to get real value of accessibility string for element.
return fb.evaluateExpressionValue(
"(id)[%s accessibilityAttributeValue:%i]" % (view, ACCESSIBILITY_LABEL_KEY),
False,
)
def accessibilityIdentifier(view):
return fb.evaluateExpressionValue(
"(id)[{} accessibilityIdentifier]".format(view), False
)
def accessibilityElements(view):
if fb.evaluateBooleanExpression(
"[UIView instancesRespondToSelector:@selector(accessibilityElements)]"
):
a11yElements = fb.evaluateExpression(
"(id)[%s accessibilityElements]" % view, False
)
if int(a11yElements, 16) != 0:
return a11yElements
if fb.evaluateBooleanExpression(
"[%s respondsToSelector:@selector(_accessibleSubviews)]" % view
):
return fb.evaluateExpression("(id)[%s _accessibleSubviews]" % (view), False)
else:
return fb.evaluateObjectExpression(
"[[[UIApplication sharedApplication] keyWindow] _accessibilityElementsInContainer:0 topLevel:%s includeKB:0]"
% view
)
def printAccessibilityHierarchy(view, indent=0):
a11yLabel = accessibilityLabel(view)
classDesc = objHelpers.className(view)
indentString = " | " * indent
# if we don't have any accessibility string - we should have some children
if int(a11yLabel.GetValue(), 16) == 0:
print(indentString + ("{} {}".format(classDesc, view)))
# We call private method that gives back all visible accessibility children
# for view
a11yElements = accessibilityElements(view)
accessibilityElementsCount = int(
fb.evaluateExpression("(int)[%s count]" % a11yElements)
)
for index in range(0, accessibilityElementsCount):
subview = fb.evaluateObjectExpression(
"[%s objectAtIndex:%i]" % (a11yElements, index)
)
printAccessibilityHierarchy(subview, indent + 1)
else:
print(
indentString
+ ("({} {}) {}".format(classDesc, view, a11yLabel.GetObjectDescription()))
)
def printAccessibilityIdentifiersHierarchy(view, indent=0):
a11yIdentifier = accessibilityIdentifier(view)
classDesc = objHelpers.className(view)
indentString = " | " * indent
# if we don't have any accessibility identifier - we should have some children
if int(a11yIdentifier.GetValue(), 16) == 0:
print(indentString + ("{} {}".format(classDesc, view)))
# We call private method that gives back all visible accessibility children
# for view
a11yElements = accessibilityElements(view)
accessibilityElementsCount = int(
fb.evaluateExpression("(int)[%s count]" % a11yElements)
)
for index in range(0, accessibilityElementsCount):
subview = fb.evaluateObjectExpression(
"[%s objectAtIndex:%i]" % (a11yElements, index)
)
printAccessibilityIdentifiersHierarchy(subview, indent + 1)
else:
print(
indentString
+ (
"({} {}) {}".format(
classDesc, view, a11yIdentifier.GetObjectDescription()
)
)
)
================================================
FILE: commands/FBAutoLayoutCommands.py
================================================
#!/usr/bin/python
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
import fbchisellldbbase as fb
import fbchisellldbviewhelpers as viewHelpers
import lldb
def lldbcommands():
return [
FBPrintAutolayoutTrace(),
FBAutolayoutBorderAmbiguous(),
FBAutolayoutUnborderAmbiguous(),
]
class FBPrintAutolayoutTrace(fb.FBCommand):
def name(self):
return "paltrace"
def description(self):
return (
"Print the Auto Layout trace for the given view. "
"Defaults to the key window."
)
def args(self):
return [
fb.FBCommandArgument(
arg="view",
type="UIView *",
help="The view to print the Auto Layout trace for.",
default="(id)[[UIApplication sharedApplication] keyWindow]",
)
]
def run(self, arguments, options):
view = fb.evaluateInputExpression(arguments[0])
opt = fb.evaluateBooleanExpression(
"[UIView instancesRespondToSelector:@selector(_autolayoutTraceRecursively:)]"
)
traceCall = "_autolayoutTraceRecursively:1" if opt else "_autolayoutTrace"
print(fb.describeObject("[{} {}]".format(view, traceCall)))
def setBorderOnAmbiguousViewRecursive(view, width, color):
if not fb.evaluateBooleanExpression(
"[(id)%s isKindOfClass:(Class)[UIView class]]" % view
):
return
isAmbiguous = fb.evaluateBooleanExpression("(BOOL)[%s hasAmbiguousLayout]" % view)
if isAmbiguous:
layer = viewHelpers.convertToLayer(view)
fb.evaluateEffect("[%s setBorderWidth:(CGFloat)%s]" % (layer, width))
fb.evaluateEffect(
"[%s setBorderColor:(CGColorRef)[(id)[UIColor %sColor] CGColor]]"
% (layer, color)
)
subviews = fb.evaluateExpression("(id)[%s subviews]" % view)
subviewsCount = int(fb.evaluateExpression("(int)[(id)%s count]" % subviews))
if subviewsCount > 0:
for i in range(0, subviewsCount):
subview = fb.evaluateExpression("(id)[%s objectAtIndex:%i]" % (subviews, i))
setBorderOnAmbiguousViewRecursive(subview, width, color)
class FBAutolayoutBorderAmbiguous(fb.FBCommand):
def name(self):
return "alamborder"
def description(self):
return "Put a border around views with an ambiguous layout"
def options(self):
return [
fb.FBCommandArgument(
short="-c",
long="--color",
arg="color",
type="string",
default="red",
help="A color name such as 'red', 'green', 'magenta', etc.",
),
fb.FBCommandArgument(
short="-w",
long="--width",
arg="width",
type="CGFloat",
default=2.0,
help="Desired width of border.",
),
]
def run(self, arguments, options):
keyWindow = fb.evaluateExpression(
"(id)[[UIApplication sharedApplication] keyWindow]"
)
setBorderOnAmbiguousViewRecursive(keyWindow, options.width, options.color)
lldb.debugger.HandleCommand("caflush")
class FBAutolayoutUnborderAmbiguous(fb.FBCommand):
def name(self):
return "alamunborder"
def description(self):
return "Removes the border around views with an ambiguous layout"
def run(self, arguments, options):
keyWindow = fb.evaluateExpression(
"(id)[[UIApplication sharedApplication] keyWindow]"
)
setBorderOnAmbiguousViewRecursive(keyWindow, 0, "red")
lldb.debugger.HandleCommand("caflush")
================================================
FILE: commands/FBClassDump.py
================================================
#!/usr/bin/python
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
import string
import fbchisellldbbase as fb
import fbchisellldbobjcruntimehelpers as runtimeHelpers
def lldbcommands():
return [FBPrintMethods(), FBPrintProperties(), FBPrintBlock()]
class FBPrintMethods(fb.FBCommand):
def name(self):
return "pmethods"
def description(self):
return "Print the class and instance methods of a class."
def options(self):
return [
fb.FBCommandArgument(
short="-a",
long="--address",
arg="showaddr",
help="Print the implementation address of the method",
default=False,
boolean=True,
),
fb.FBCommandArgument(
short="-i",
long="--instance",
arg="insmethod",
help="Print the instance methods",
default=False,
boolean=True,
),
fb.FBCommandArgument(
short="-c",
long="--class",
arg="clsmethod",
help="Print the class methods",
default=False,
boolean=True,
),
fb.FBCommandArgument(
short="-n",
long="--name",
arg="clsname",
help="Take the argument as class name",
default=False,
boolean=True,
),
]
def args(self):
return [
fb.FBCommandArgument(
arg="instance or class",
type="instance or Class",
help="an Objective-C Class.",
)
]
def run(self, arguments, options):
cls = getClassFromArgument(arguments[0], options.clsname)
if options.clsmethod:
print("Class Methods:")
printClassMethods(cls, options.showaddr)
if options.insmethod:
print("\nInstance Methods:")
printInstanceMethods(cls, options.showaddr)
if not options.clsmethod and not options.insmethod:
print("Class Methods:")
printClassMethods(cls, options.showaddr)
print("\nInstance Methods:")
printInstanceMethods(cls, options.showaddr)
class FBPrintProperties(fb.FBCommand):
def name(self):
return "pproperties"
def description(self):
return "Print the properties of an instance or Class"
def options(self):
return [
fb.FBCommandArgument(
short="-n",
long="--name",
arg="clsname",
help="Take the argument as class name",
default=False,
boolean=True,
)
]
def args(self):
return [
fb.FBCommandArgument(
arg="instance or class",
type="instance or Class",
help="an Objective-C Class.",
)
]
def run(self, arguments, options):
cls = getClassFromArgument(arguments[0], options.clsname)
printProperties(cls)
class FBPrintBlock(fb.FBCommand):
def name(self):
return "pblock"
def description(self):
return "Print the block`s implementation address and signature"
def args(self):
return [
fb.FBCommandArgument(arg="block", help="The block object you want to print")
]
def run(self, arguments, options):
block = arguments[0]
# http://clang.llvm.org/docs/Block-ABI-Apple.html
tmpString = """
enum {
BLOCK_HAS_COPY_DISPOSE = (1 << 25),
BLOCK_HAS_CTOR = (1 << 26), // helpers have C++ code
BLOCK_IS_GLOBAL = (1 << 28),
BLOCK_HAS_STRET = (1 << 29), // IFF BLOCK_HAS_SIGNATURE
BLOCK_HAS_SIGNATURE = (1 << 30),
};
struct Block_literal_1 {
void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor_1 {
unsigned long int reserved; // NULL
unsigned long int size; // sizeof(struct Block_literal_1)
// optional helper functions
void (*copy_helper)(void *dst, void *src); // IFF (1<<25)
void (*dispose_helper)(void *src); // IFF (1<<25)
// required ABI.2010.3.16
const char *signature; // IFF (1<<30)
} *descriptor;
// imported variables
};
struct Block_literal_1 real = *((__bridge struct Block_literal_1 *)$block);
NSMutableDictionary *dict = (id)[NSMutableDictionary dictionary];
[dict setObject:(id)[NSNumber numberWithLong:(long)real.invoke] forKey:@"invoke"];
if (real.flags & BLOCK_HAS_SIGNATURE) {
char *signature;
if (real.flags & BLOCK_HAS_COPY_DISPOSE) {
signature = (char *)(real.descriptor)->signature;
} else {
signature = (char *)(real.descriptor)->copy_helper;
}
NSMethodSignature *sig = [NSMethodSignature signatureWithObjCTypes:signature];
NSMutableArray *types = [NSMutableArray array];
[types addObject:(id)[NSString stringWithUTF8String:(char *)[sig methodReturnType]]];
for (NSUInteger i = 0; i < sig.numberOfArguments; i++) {
char *type = (char *)[sig getArgumentTypeAtIndex:i];
[types addObject:(id)[NSString stringWithUTF8String:type]];
}
[dict setObject:types forKey:@"signature"];
}
RETURN(dict);
"""
command = string.Template(tmpString).substitute(block=block)
json = fb.evaluate(command)
signature = json["signature"]
if not signature:
print("Imp: " + hex(json["invoke"]))
return
sigStr = "{} ^(".format(decode(signature[0]))
# the block`s implementation always take the block as it`s first argument, so we ignore it
sigStr += ", ".join([decode(m) for m in signature[2:]])
sigStr += ");"
print("Imp: " + hex(json["invoke"]) + " Signature: " + sigStr)
# helpers
def isClassObject(arg):
return runtimeHelpers.class_isMetaClass(runtimeHelpers.object_getClass(arg))
def getClassFromArgument(arg, is_classname):
cls = arg
if is_classname:
cls = runtimeHelpers.objc_getClass(cls)
if not int(cls, 16):
raise Exception('Class "{}" not found'.format(arg))
else:
if not isClassObject(cls):
cls = runtimeHelpers.object_getClass(cls)
if not isClassObject(cls):
raise Exception(
"Invalid argument. Please specify an instance or a Class."
)
return cls
def printInstanceMethods(cls, showaddr=False, prefix="-"):
methods = getMethods(cls)
if not methods:
print("No methods were found")
for m in methods:
if showaddr:
print(prefix + " " + m.prettyPrintString() + " " + str(m.imp))
else:
print(prefix + " " + m.prettyPrintString())
def printClassMethods(cls, showaddr=False):
printInstanceMethods(runtimeHelpers.object_getClass(cls), showaddr, "+")
def printProperties(cls, showvalue=False):
props = getProperties(cls)
for p in props:
print(p.prettyPrintString())
def decode(code):
encodeMap = {
"c": "char",
"i": "int",
"s": "short",
"l": "long",
"q": "long long",
"C": "unsigned char",
"I": "unsigned int",
"S": "unsigned short",
"L": "unsigned long",
"Q": "unsigned long long",
"f": "float",
"d": "double",
"B": "bool",
"v": "void",
"*": "char *",
"@": "id",
"#": "Class",
":": "SEL",
}
ret = code
if code in encodeMap:
ret = encodeMap[code]
elif ret[0:1] == "@":
if ret[1:2] == "?": # @? represent a block
ret = code
elif ret[2:3] == "<": # @"<aDelegate><bDelegate>"
ret = "id" + ret[2:-1].replace("><", ", ")
else:
ret = ret[2:-1] + " *"
elif ret[0:1] == "^":
ret = decode(ret[1:]) + " *"
return ret
# Notice that evaluateExpression doesn't work with variable arguments. such as -[NSString stringWithFormat:]
# I remove the "free(methods)" because it would cause evaluateExpressionValue to raise exception some time.
def getMethods(klass):
tmpString = """
unsigned int outCount;
Method *methods = (Method *)class_copyMethodList((Class)$cls, &outCount);
NSMutableArray *result = (id)[NSMutableArray array];
for (int i = 0; i < outCount; i++) {
NSMutableDictionary *m = (id)[NSMutableDictionary dictionary];
SEL name = (SEL)method_getName(methods[i]);
[m setObject:(id)NSStringFromSelector(name) forKey:@"name"];
char * encoding = (char *)method_getTypeEncoding(methods[i]);
[m setObject:(id)[NSString stringWithUTF8String:encoding] forKey:@"type_encoding"];
NSMutableArray *types = (id)[NSMutableArray array];
NSInteger args = (NSInteger)method_getNumberOfArguments(methods[i]);
for (int idx = 0; idx < args; idx++) {
char *type = (char *)method_copyArgumentType(methods[i], idx);
[types addObject:(id)[NSString stringWithUTF8String:type]];
}
[m setObject:types forKey:@"parameters_type"];
char *ret_type = (char *)method_copyReturnType(methods[i]);
[m setObject:(id)[NSString stringWithUTF8String:ret_type] forKey:@"return_type"];
long imp = (long)method_getImplementation(methods[i]);
[m setObject:[NSNumber numberWithLongLong:imp] forKey:@"implementation"];
[result addObject:m];
}
RETURN(result);
"""
command = string.Template(tmpString).substitute(cls=klass)
methods = fb.evaluate(command)
return [Method(m) for m in methods]
class Method:
def __init__(self, json):
self.name = json["name"]
self.type_encoding = json["type_encoding"]
self.parameters_type = json["parameters_type"]
self.return_type = json["return_type"]
self.imp = self.toHex(json["implementation"])
def prettyPrintString(self):
argnum = len(self.parameters_type)
names = self.name.split(":")
# the argnum count must be bigger then 2, index 0 for self, index 1 for SEL
for i in range(2, argnum):
arg_type = self.parameters_type[i]
names[i - 2] = names[i - 2] + ":(" + decode(arg_type) + ")arg" + str(i - 2)
string = " ".join(names)
return "({}){}".format(decode(self.return_type), string)
def toHex(self, addr):
return hex(addr)
def __str__(self):
return (
"<Method:"
+ self.oc_method
+ "> "
+ self.name
+ " --- "
+ self.type
+ " --- "
+ self.imp
)
def getProperties(klass):
tmpString = """
NSMutableArray *result = (id)[NSMutableArray array];
unsigned int count;
objc_property_t *props = (objc_property_t *)class_copyPropertyList((Class)$cls, &count);
for (int i = 0; i < count; i++) {
NSMutableDictionary *dict = (id)[NSMutableDictionary dictionary];
char *name = (char *)property_getName(props[i]);
[dict setObject:(id)[NSString stringWithUTF8String:name] forKey:@"name"];
char *attrstr = (char *)property_getAttributes(props[i]);
[dict setObject:(id)[NSString stringWithUTF8String:attrstr] forKey:@"attributes_string"];
NSMutableDictionary *attrsDict = (id)[NSMutableDictionary dictionary];
unsigned int pcount;
objc_property_attribute_t *attrs = (objc_property_attribute_t *)property_copyAttributeList(props[i], &pcount);
for (int i = 0; i < pcount; i++) {
NSString *name = (id)[NSString stringWithUTF8String:(char *)attrs[i].name];
NSString *value = (id)[NSString stringWithUTF8String:(char *)attrs[i].value];
[attrsDict setObject:value forKey:name];
}
[dict setObject:attrsDict forKey:@"attributes"];
[result addObject:dict];
}
RETURN(result);
"""
command = string.Template(tmpString).substitute(cls=klass)
propsJson = fb.evaluate(command)
return [Property(m) for m in propsJson]
class Property:
def __init__(self, json):
self.name = json["name"]
self.attributes_string = json["attributes_string"]
self.attributes = json["attributes"]
# https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/TP40008048-CH101-SW1
def prettyPrintString(self):
attrs = []
if "N" in self.attributes:
attrs.append("nonatomic")
else:
attrs.append("atomic")
if "&" in self.attributes:
attrs.append("strong")
elif "C" in self.attributes:
attrs.append("copy")
elif "W" in self.attributes:
attrs.append("weak")
else:
attrs.append("assign")
if "R" in self.attributes:
attrs.append("readonly")
if "G" in self.attributes:
attrs.append("getter={}".format(self.attributes["G"]))
if "S" in self.attributes:
attrs.append("setter={}".format(self.attributes["S"]))
return "@property ({}) {} {};".format(
", ".join(attrs), decode(self.attributes["T"]), self.name
)
================================================
FILE: commands/FBComponentCommands.py
================================================
#!/usr/bin/python
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
import fbchisellldbbase as fb
import fbchisellldbviewhelpers as viewHelpers
def lldbcommands():
return [
FBComponentsDebugCommand(),
FBComponentsPrintCommand(),
FBComponentsReflowCommand(),
]
class FBComponentsDebugCommand(fb.FBCommand):
def name(self):
return "dcomponents"
def description(self):
return "Set debugging options for components."
def options(self):
return [
fb.FBCommandArgument(
short="-s",
long="--set",
arg="set",
help="Set debug mode for components",
boolean=True,
),
fb.FBCommandArgument(
short="-u",
long="--unset",
arg="unset",
help="Unset debug mode for components",
boolean=True,
),
]
def run(self, arguments, options):
print("Debug mode for ComponentKit is deprecated; use Flipper instead.")
class FBComponentsPrintCommand(fb.FBCommand):
def name(self):
return "pcomponents"
def description(self):
return (
"Print a recursive description of components found starting from <aView>."
)
def args(self):
return [
fb.FBCommandArgument(
arg="aView",
type="UIView* or CKComponent*",
help="The view or component from which the search for components begins.",
default="(id)[[UIApplication sharedApplication] keyWindow]",
)
]
def run(self, arguments, options):
view = fb.evaluateInputExpression(arguments[0])
if not viewHelpers.isView(view):
# assume it's a CKComponent
view = fb.evaluateExpression("((CKComponent *)%s).viewContext.view" % view)
print(
fb.describeObject(
"[CKComponentHierarchyDebugHelper componentHierarchyDescriptionForView:(UIView *)"
+ view
+ "]"
)
)
class FBComponentsReflowCommand(fb.FBCommand):
def name(self):
return "rcomponents"
def description(self):
return "Synchronously reflow and update all components."
def run(self, arguments, options):
fb.evaluateEffect("[CKComponentDebugController reflowComponents]")
================================================
FILE: commands/FBCopyCommands.py
================================================
#!/usr/bin/python
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
from __future__ import print_function
import errno
import os
import time
import fbchisellldbbase as fb
import fbchisellldbobjecthelpers as objectHelpers
import lldb
def lldbcommands():
return [FBCopyCommand()]
def _copyFromURL(url, preferredFilename, noOpen):
data = fb.evaluateObjectExpression(
"(id)[NSData dataWithContentsOfURL:(id){}]".format(url)
)
defaultFilename = fb.describeObject(
"(id)[[{} pathComponents] lastObject]".format(url)
)
_copyFromData(data, defaultFilename, preferredFilename, noOpen)
def _copyFromData(data, defaultFilename, preferredFilename, noOpen):
directory = "/tmp/chisel_copy/"
path = directory + (preferredFilename or defaultFilename)
try:
os.makedirs(directory)
except OSError as e:
if e.errno == errno.EEXIST and os.path.isdir(directory):
pass
else:
raise
startAddress = fb.evaluateExpression("(void *)[(id)" + data + " bytes]")
length = fb.evaluateExpression("(NSUInteger)[(id)" + data + " length]")
address = int(startAddress, 16)
length = int(length)
if not (address or length):
print("Could not get data.")
return
process = lldb.debugger.GetSelectedTarget().GetProcess()
error = lldb.SBError()
mem = process.ReadMemory(address, length, error)
if error is not None and str(error) != "success":
print(error)
else:
with open(path, "wb") as file:
file.write(mem)
file.close()
print(path)
if not noOpen:
os.system("open " + path)
def _copy(target, preferredFilename, noOpen):
target = "(" + target + ")"
if objectHelpers.isKindOfClass(target, "NSURL"):
_copyFromURL(target, preferredFilename, noOpen)
elif objectHelpers.isKindOfClass(target, "NSData"):
_copyFromData(
target,
time.strftime("%Y-%m-%d-%H-%M-%S", time.gmtime()) + ".data",
preferredFilename,
noOpen,
)
else:
print(
"{} isn't supported. You can copy an NSURL or NSData.".format(
objectHelpers.className(target)
)
)
class FBCopyCommand(fb.FBCommand):
def name(self):
return "copy"
def description(self):
return "Copy data to your Mac."
def options(self):
return [
fb.FBCommandArgument(
short="-f",
long="--filename",
arg="filename",
help="The output filename.",
),
fb.FBCommandArgument(
short="-n",
long="--no-open",
arg="noOpen",
boolean=True,
default=False,
help="Do not open the file.",
),
]
def args(self):
return [
fb.FBCommandArgument(arg="target", type="(id)", help="The object to copy.")
]
def run(self, arguments, options):
_copy(arguments[0], options.filename, options.noOpen)
================================================
FILE: commands/FBCounterCommands.py
================================================
#!/usr/bin/python
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# These set of commands provide a way to use counters in debug time. By using these counters,
# you can track how many times your program takes a specific path.
#
# Sample Use Case:
# Let's say you have a function that logs some messages from various parts of your code.
# And you want to learn how many times logMessage is called on startup.
#
# 1. Add a breakpoint to the entry point of your program (e.g. main).
# a. Add `zzz 10 printcounter` as an action.
# b. Check "Automatically continue after evaluating actions"
# 2. Add a breakpoint to the logMessage function.
# a. Add `incrementcounter log` as an action.
# b. Add `incrementcounter log_{} message` as an action.
# c. Check "Automatically continue after evaluating actions"
# 3. Run the program
#
# Format String:
# It uses Python's string.Formatter to format strings. You can use placeholders here as you can in Python:
# https://docs.python.org/3.4/library/string.html#string.Formatter.format
#
# Sample key_format_string:
# "key_{}" (int)5 -> Will build the key string as "key_5"
# Can be removed when Python 2 support is removed.
from __future__ import print_function
import fbchisellldbbase as fb
counters = {}
def lldbcommands():
return [
FBIncrementCounterCommand(),
FBPrintCounterCommand(),
FBPrintCountersCommand(),
FBResetCounterCommand(),
FBResetCountersCommand(),
]
def generateKey(arguments):
keyFormatString = arguments[1]
keyArgs = []
for argument in arguments[2:]:
if argument.startswith("("):
value = fb.evaluateExpression(argument)
else:
value = fb.evaluateExpressionValue(argument).GetObjectDescription()
if not value:
value = fb.evaluateExpression(argument)
keyArgs.append(value)
return keyFormatString.format(*keyArgs).strip()
# Increments the counter for the key.
# (lldb) incrementcounter key_format_string key_args
class FBIncrementCounterCommand(fb.FBCommand):
def name(self):
return "incrementcounter"
def description(self):
return "Increments the counter for the key."
def run(self, arguments, options):
key = generateKey(arguments)
counters[key] = counters.get(key, 0) + 1
# Prints the counter for the key.
# (lldb) printcounter key_format_string key_args
# 0
class FBPrintCounterCommand(fb.FBCommand):
def name(self):
return "printcounter"
def description(self):
return "Prints the counter for the key."
def run(self, arguments, options):
key = generateKey(arguments)
print(str(counters[key]))
# Prints all the counters sorted by the keys.
# (lldb) printcounters
# key_1: 0
class FBPrintCountersCommand(fb.FBCommand):
def name(self):
return "printcounters"
def description(self):
return "Prints all the counters sorted by the keys."
def run(self, arguments, options):
keys = sorted(counters.keys())
for key in keys:
print(key + ": " + str(counters[key]))
# Resets the counter for the key.
# (lldb) resetcounter key_format_string key_args
class FBResetCounterCommand(fb.FBCommand):
def name(self):
return "resetcounter"
def description(self):
return "Resets the counter for the key."
def run(self, arguments, options):
key = generateKey(arguments)
counters[key] = 0
# Resets all the counters.
# (lldb) resetcounters
class FBResetCountersCommand(fb.FBCommand):
def name(self):
return "resetcounters"
def description(self):
return "Resets all the counters."
def run(self, arguments, options):
counters.clear()
================================================
FILE: commands/FBDebugCommands.py
================================================
#!/usr/bin/python
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# Can be removed when Python 2 support is removed.
from __future__ import print_function
import os
import re
import sys
import fbchisellldbbase as fb
import fbchisellldbobjcruntimehelpers as objc
import lldb
def lldbcommands():
return [
FBWatchInstanceVariableCommand(),
FBFrameworkAddressBreakpointCommand(),
FBMethodBreakpointCommand(),
FBMemoryWarningCommand(),
FBFindInstancesCommand(),
FBMethodBreakpointEnableCommand(),
FBMethodBreakpointDisableCommand(),
FBHeapFromCommand(),
FBSequenceCommand(),
]
class FBWatchInstanceVariableCommand(fb.FBCommand):
def name(self):
return "wivar"
def description(self):
return "Set a watchpoint for an object's instance variable."
def args(self):
return [
fb.FBCommandArgument(
arg="object", type="id", help="Object expression to be evaluated."
),
fb.FBCommandArgument(
arg="ivarName", help="Name of the instance variable to watch."
),
]
def run(self, arguments, options):
commandForObject, ivarName = arguments
objectAddress = int(fb.evaluateObjectExpression(commandForObject), 0)
ivarOffsetCommand = '(ptrdiff_t)ivar_getOffset((void*)object_getInstanceVariable((id){}, "{}", 0))'.format(
objectAddress, ivarName
)
ivarOffset = int(fb.evaluateExpression(ivarOffsetCommand), 0)
# A multi-statement command allows for variables scoped to the command,
# not permanent in the session like $variables.
ivarSizeCommand = (
"unsigned int size = 0;"
'char *typeEncoding = (char *)ivar_getTypeEncoding((void*)class_getInstanceVariable((Class)object_getClass((id){}), "{}"));'
"(char *)NSGetSizeAndAlignment(typeEncoding, &size, 0);"
"size"
).format(objectAddress, ivarName)
ivarSize = int(fb.evaluateExpression(ivarSizeCommand), 0)
error = lldb.SBError()
watchpoint = lldb.debugger.GetSelectedTarget().WatchAddress(
objectAddress + ivarOffset, ivarSize, False, True, error
)
if error.Success():
print(
"Remember to delete the watchpoint using: watchpoint delete {}".format(
watchpoint.GetID()
)
)
else:
print("Could not create the watchpoint: {}".format(error.GetCString()))
class FBFrameworkAddressBreakpointCommand(fb.FBCommand):
def name(self):
return "binside"
def description(self):
return "Set a breakpoint for a relative address within the framework/library that's currently running. This does the work of finding the offset for the framework/library and sliding your address accordingly."
def args(self):
return [
fb.FBCommandArgument(
arg="address",
type="string",
help="Address within the currently running framework to set a breakpoint on.",
)
]
def run(self, arguments, options):
library_address = int(arguments[0], 0)
address = int(
lldb.debugger.GetSelectedTarget()
.GetProcess()
.GetSelectedThread()
.GetSelectedFrame()
.GetModule()
.ResolveFileAddress(library_address)
)
lldb.debugger.HandleCommand("breakpoint set --address {}".format(address))
class FBMethodBreakpointCommand(fb.FBCommand):
def name(self):
return "bmessage"
def description(self):
return "Set a breakpoint for a selector on a class, even if the class itself doesn't override that selector. It walks the hierarchy until it finds a class that does implement the selector and sets a conditional breakpoint there."
def args(self):
return [
fb.FBCommandArgument(
arg="expression",
type="string",
help='Expression to set a breakpoint on, e.g. "-[MyView setFrame:]", "+[MyView awesomeClassMethod]" or "-[0xabcd1234 setFrame:]"',
)
]
def run(self, arguments, options):
expression = arguments[0]
methodPattern = re.compile(
r"""
(?P<scope>[-+])?
\[
(?P<target>.*?)
(?P<category>\(.+\))?
\s+
(?P<selector>.*)
\]
""",
re.VERBOSE,
)
match = methodPattern.match(expression)
if not match:
print("Failed to parse expression. Do you even Objective-C?!")
return
expressionForSelf = objc.functionPreambleExpressionForSelf()
if not expressionForSelf:
arch = objc.currentArch()
print(
"Your architecture, {}, is truly fantastic. However, I don't currently support it.".format(
arch
)
)
return
methodTypeCharacter = match.group("scope")
classNameOrExpression = match.group("target")
category = match.group("category")
selector = match.group("selector")
methodIsClassMethod = methodTypeCharacter == "+"
if methodIsClassMethod:
methodTypeCharacter = "\+"
else:
# The default is instance method, and methodTypeCharacter
# may not actually be '-'.
methodTypeCharacter = "-"
targetIsClass = False
targetObject = fb.evaluateObjectExpression(
"({})".format(classNameOrExpression), False
)
if not targetObject:
# If the expression didn't yield anything then it's likely a class.
# Assume it is. We will check again that the class does actually
# exist anyway.
targetIsClass = True
targetObject = fb.evaluateObjectExpression(
"[{} class]".format(classNameOrExpression), False
)
targetClass = fb.evaluateObjectExpression(
"[{} class]".format(targetObject), False
)
if not targetClass or int(targetClass, 0) == 0:
print(
'Couldn\'t find a class from the expression "{}". Did you typo?'.format(
classNameOrExpression
)
)
return
if methodIsClassMethod:
targetClass = objc.object_getClass(targetClass)
found = False
nextClass = targetClass
while not found and int(nextClass, 0) > 0:
if classItselfImplementsSelector(nextClass, selector):
found = True
else:
nextClass = objc.class_getSuperclass(nextClass)
if not found:
print(
"There doesn't seem to be an implementation of {} in the class hierarchy. Made a boo boo with the selector name?".format(
selector
)
)
return
breakpointClassName = objc.class_getName(nextClass)
formattedCategory = category if category else ""
breakpointFullName = "{}[{}{} {}]".format(
methodTypeCharacter, breakpointClassName, formattedCategory, selector
)
if targetIsClass:
breakpointCondition = "(void*)object_getClass({}) == {}".format(
expressionForSelf, targetClass
)
else:
breakpointCondition = "(void*){} == {}".format(
expressionForSelf, targetObject
)
print(
"Setting a breakpoint at {} with condition {}".format(
breakpointFullName, breakpointCondition
)
)
if category:
lldb.debugger.HandleCommand(
'breakpoint set --skip-prologue false --fullname "{}" --condition "{}"'.format(
breakpointFullName, breakpointCondition
)
)
else:
breakpointPattern = r"{}\[{}(\(.+\))? {}\]".format(
methodTypeCharacter, breakpointClassName, selector
)
lldb.debugger.HandleCommand(
'breakpoint set --skip-prologue false --func-regex "{}" --condition "{}"'.format(
breakpointPattern, breakpointCondition
)
)
def classItselfImplementsSelector(klass, selector):
thisMethod = objc.class_getInstanceMethod(klass, selector)
if thisMethod == 0:
return False
superklass = objc.class_getSuperclass(klass)
superMethod = objc.class_getInstanceMethod(superklass, selector)
if thisMethod == superMethod:
return False
else:
return True
class FBMemoryWarningCommand(fb.FBCommand):
def name(self):
return "mwarning"
def description(self):
return "simulate a memory warning"
def run(self, arguments, options):
fb.evaluateEffect(
"[[UIApplication sharedApplication] performSelector:@selector(_performMemoryWarning)]"
)
def switchBreakpointState(expression, on):
expression_pattern = re.compile(r"{}".format(expression), re.I)
target = lldb.debugger.GetSelectedTarget()
for breakpoint in target.breakpoint_iter():
if breakpoint.IsEnabled() != on and (
expression_pattern.search(str(breakpoint))
):
print(str(breakpoint))
breakpoint.SetEnabled(on)
for location in breakpoint:
if location.IsEnabled() != on and (
expression_pattern.search(str(location))
or expression == hex(location.GetLoadAddress())
):
print(str(location))
location.SetEnabled(on)
class FBMethodBreakpointEnableCommand(fb.FBCommand):
def name(self):
return "benable"
def description(self):
return """
Enable a set of breakpoints for a regular expression
Examples:
* benable ***address***
benable 0x0000000104514dfc
benable 0x183e23564
#use `benable *filename*` to switch all breakpoints in this file to `enable`
benable SUNNetService.m
#use `benable ***module(AppName)***` to switch all breakpoints in this module to `enable`
benable UIKit
benable Foundation
"""
def args(self):
return [
fb.FBCommandArgument(
arg="expression", type="string", help="Expression to enable breakpoint"
)
]
def run(self, arguments, options):
expression = arguments[0]
switchBreakpointState(expression, True)
class FBMethodBreakpointDisableCommand(fb.FBCommand):
def name(self):
return "bdisable"
def description(self):
return """
Disable a set of breakpoints for a regular expression
Examples:
* bdisable ***address***
bdisable 0x0000000104514dfc
bdisable 0x183e23564
#use `bdisable *filename*` to switch all breakpoints in this file to `disable`
bdisable SUNNetService.m
#use `bdisable ***module(AppName)***` to switch all breakpoints in this module to `disable`
bdisable UIKit
bdisable Foundation
"""
def args(self):
return [
fb.FBCommandArgument(
arg="expression", type="string", help="Expression to disable breakpoint"
)
]
def run(self, arguments, options):
expression = arguments[0]
switchBreakpointState(expression, False)
class FBFindInstancesCommand(fb.FBCommand):
def name(self):
return "findinstances"
def args(self):
return [
fb.FBCommandArgument(arg="type", help="Class or protocol name"),
fb.FBCommandArgument(
arg="query",
default=" ", # space is a hack to mark optional
help="Query expression, uses NSPredicate syntax",
),
]
def description(self):
return """
Find instances of specified ObjC classes.
This command scans memory and uses heuristics to identify instances of
Objective-C classes. This includes Swift classes that descend from NSObject.
Basic examples:
findinstances UIScrollView
findinstances *UIScrollView
findinstances UIScrollViewDelegate
These basic searches find instances of the given class or protocol. By
default, subclasses of the class or protocol are included in the results. To
find exact class instances, add a `*` prefix, for example: *UIScrollView.
Advanced examples:
# Find views that are either: hidden, invisible, or not in a window
findinstances UIView hidden == true || alpha == 0 || window == nil
# Find views that have either a zero width or zero height
findinstances UIView layer.bounds.#size.width == 0 || layer.bounds.#size.height == 0
# Find leaf views that have no subviews
findinstances UIView subviews.@count == 0
# Find dictionaries that have keys that might be passwords or passphrases
findinstances NSDictionary any @allKeys beginswith 'pass'
These examples make use of a filter. The filter is implemented with
NSPredicate, see its documentaiton for more details. Basic NSPredicate
expressions have relatively predicatable syntax. There are some exceptions
as seen above, see https://github.com/facebook/chisel/wiki/findinstances.
"""
def lex(self, commandLine):
# Can't use default shlex splitting because it strips quotes, which results
# in invalid NSPredicate syntax. Split the input into type and rest (query).
return commandLine.split(" ", 1)
def run(self, arguments, options):
if not self.loadChiselIfNecessary():
return
if len(arguments) == 0 or not arguments[0].strip():
print(
"Usage: findinstances <classOrProtocol> [<predicate>]; Run `help findinstances`"
)
return
query = arguments[0]
predicate = arguments[1].strip()
# Escape double quotes and backslashes.
predicate = re.sub('([\\"])', r"\\\1", predicate)
call = '(void)PrintInstances("{}", "{}")'.format(query, predicate)
fb.evaluateExpressionValue(call)
def loadChiselIfNecessary(self):
target = lldb.debugger.GetSelectedTarget()
symbol_contexts = target.FindSymbols("PrintInstances", lldb.eSymbolTypeCode)
if any(ctx.symbol.IsValid() for ctx in symbol_contexts):
return True
path = self.chiselLibraryPath()
if not os.path.exists(path):
print("Chisel library missing: " + path)
return False
module = fb.evaluateExpressionValue('(void*)dlopen("{}", 2)'.format(path))
if module.unsigned != 0 or target.module["Chisel"]:
return True
# `errno` is a macro that expands to a call to __error(). In development,
# lldb was not getting a correct value for `errno`, so `__error()` is used.
errno = fb.evaluateExpressionValue("*(int*)__error()").value
error = fb.evaluateExpressionValue("(char*)dlerror()")
if errno == 50:
# KERN_CODESIGN_ERROR from <mach/kern_return.h>
print("Error loading Chisel: Code signing failure; Must re-run codesign")
elif error.unsigned != 0:
print("Error loading Chisel: " + error.summary)
elif errno != 0:
error = fb.evaluateExpressionValue("(char*)strerror({})".format(errno))
if error.unsigned != 0:
print("Error loading Chisel: " + error.summary)
else:
print("Error loading Chisel (errno {})".format(errno))
else:
print("Unknown error loading Chisel")
return False
def chiselLibraryPath(self):
# script os.environ['CHISEL_LIBRARY_PATH'] = '/path/to/custom/Chisel'
path = os.getenv("CHISEL_LIBRARY_PATH")
if path and os.path.exists(path):
return path
source_path = sys.modules[__name__].__file__
source_dir = os.path.dirname(source_path)
# ugh: ../.. is to back out of commands/, then back out of libexec/
return os.path.join(source_dir, "..", "..", "lib", "Chisel.framework", "Chisel")
class FBHeapFromCommand(fb.FBCommand):
def name(self):
return "heapfrom"
def description(self):
return "Show all nested heap pointers contained within a given variable."
def run(self, arguments, options):
# This command is like `expression --synthetic-type false`,
# except only showing nested heap references.
var = self.context.frame.var(arguments[0])
if not var or not var.IsValid():
self.result.SetError('No variable named "{}"'.format(arguments[0]))
return
# Use the actual underlying structure of the variable,
# not the human friendly (synthetic) one.
root = var.GetNonSyntheticValue()
# Traversal of SBValue tree to get leaf nodes, which is where heap
# pointers will be.
leafs = []
queue = [root]
while queue:
node = queue.pop(0)
if node.num_children == 0:
leafs.append(node)
else:
queue += [node.GetChildAtIndex(i) for i in range(node.num_children)]
pointers = {}
for node in leafs:
# Assumption: an addr that has no value means a pointer.
if node.addr and not node.value:
pointers[node.load_addr] = node.path
options = lldb.SBExpressionOptions()
options.SetLanguage(lldb.eLanguageTypeC)
def isHeap(addr):
lookup = "(int)malloc_size({})".format(addr)
return self.context.frame.EvaluateExpression(lookup, options).unsigned != 0
allocations = (addr for addr in pointers if isHeap(addr))
for addr in allocations:
print(
"0x{addr:x} {path}".format(addr=addr, path=pointers[addr]),
file=self.result,
)
if not allocations:
print("No heap addresses found", file=self.result)
class FBSequenceCommand(fb.FBCommand):
def name(self):
return "sequence"
def description(self):
return "Run commands in sequence, stopping on any error."
def lex(self, commandLine):
return [command.strip() for command in commandLine.split(";")]
def run(self, arguments, options):
# arguments contains the raw command first, followed by the split commands.
if len(arguments) == 1:
return
commands = filter(None, arguments[1:])
interpreter = lldb.debugger.GetCommandInterpreter()
# Complete one command before running the next one in the sequence. Disable
# async to do this. Also, save the current async value to restore it later.
asyncFlag = lldb.debugger.GetAsync()
lldb.debugger.SetAsync(False)
for command in commands[:-1]:
success = self.run_command(interpreter, command)
if not success:
lldb.debugger.SetAsync(asyncFlag)
return
# Restore original async value.
lldb.debugger.SetAsync(asyncFlag)
# If the last command is `continue`, call Continue() on the process
# instead. This is done because HandleCommand('continue') has strange
# behavior, while calling Continue() works as expected.
last = commands[-1]
if self.is_continue(interpreter, last):
self.context.process.Continue()
else:
self.run_command(interpreter, last)
def run_command(self, interpreter, command):
ret = lldb.SBCommandReturnObject()
interpreter.HandleCommand(command, ret)
if ret.GetOutput():
print(ret.GetOutput().strip(), file=self.result)
if ret.Succeeded():
return True
self.result.SetError(ret.GetError())
self.result.SetStatus(ret.GetStatus())
return False
def is_continue(self, interpreter, command):
ret = lldb.SBCommandReturnObject()
interpreter.ResolveCommand(command, ret)
return ret.GetOutput() == "process continue"
================================================
FILE: commands/FBDelay.py
================================================
#!/usr/bin/python
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
from threading import Timer
import fbchisellldbbase as fb
import lldb
def lldbcommands():
return [FBDelay()]
class FBDelay(fb.FBCommand):
def name(self):
return "zzz"
def description(self):
return "Executes specified lldb command after delay."
def args(self):
return [
fb.FBCommandArgument(
arg="delay in seconds",
type="float",
help="time to wait before executing specified command",
),
fb.FBCommandArgument(
arg="lldb command",
type="string",
help="another lldb command to execute after specified delay",
default="process interrupt",
),
]
def run(self, arguments, options):
lldb.debugger.SetAsync(True)
lldb.debugger.HandleCommand("process continue")
delay = float(arguments[0])
command = str(arguments[1])
t = Timer(delay, lambda: self.runDelayed(command))
t.start()
def runDelayed(self, command):
lldb.debugger.HandleCommand("process interrupt")
lldb.debugger.HandleCommand(command)
================================================
FILE: commands/FBDisplayCommands.py
================================================
#!/usr/bin/python
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
import fbchisellldbbase as fb
import fbchisellldbobjcruntimehelpers as runtimeHelpers
import fbchisellldbviewcontrollerhelpers as viewControllerHelpers
import fbchisellldbviewhelpers as viewHelpers
import lldb
def lldbcommands():
return [
FBCoreAnimationFlushCommand(),
FBDrawBorderCommand(),
FBRemoveBorderCommand(),
FBMaskViewCommand(),
FBUnmaskViewCommand(),
FBShowViewCommand(),
FBHideViewCommand(),
FBPresentViewControllerCommand(),
FBDismissViewControllerCommand(),
FBSlowAnimationCommand(),
FBUnslowAnimationCommand(),
]
class FBDrawBorderCommand(fb.FBCommand):
colors = [
"black",
"gray",
"red",
"green",
"blue",
"cyan",
"yellow",
"magenta",
"orange",
"purple",
"brown",
]
def name(self):
return "border"
def description(self):
return "Draws a border around <viewOrLayer>. Color and width can be optionally provided. Additionally depth can be provided in order to recursively border subviews."
def args(self):
return [
fb.FBCommandArgument(
arg="viewOrLayer",
type="UIView/NSView/CALayer *",
help="The view/layer to border. NSViews must be layer-backed.",
)
]
def options(self):
return [
fb.FBCommandArgument(
short="-c",
long="--color",
arg="color",
type="string",
default="red",
help="A color name such as 'red', 'green', 'magenta', etc.",
),
fb.FBCommandArgument(
short="-w",
long="--width",
arg="width",
type="CGFloat",
default=2.0,
help="Desired width of border.",
),
fb.FBCommandArgument(
short="-d",
long="--depth",
arg="depth",
type="int",
default=0,
help="Number of levels of subviews to border. Each level gets a different color beginning with the provided or default color",
),
]
def run(self, args, options):
def setBorder(layer, width, color, colorClass):
fb.evaluateEffect("[%s setBorderWidth:(CGFloat)%s]" % (layer, width))
fb.evaluateEffect(
"[%s setBorderColor:(CGColorRef)[(id)[%s %sColor] CGColor]]"
% (layer, colorClass, color)
)
obj = fb.evaluateInputExpression(args[0])
depth = int(options.depth)
isMac = runtimeHelpers.isMacintoshArch()
color = options.color
assert color in self.colors, "Color must be one of the following: {}".format(
" ".join(self.colors)
)
colorClassName = "UIColor"
if isMac:
colorClassName = "NSColor"
if viewHelpers.isView(obj):
prevLevel = 0
for view, level in viewHelpers.subviewsOfView(obj):
if level > depth:
break
if prevLevel != level:
color = self.nextColorAfterColor(color)
prevLevel = level
layer = viewHelpers.convertToLayer(view)
setBorder(layer, options.width, color, colorClassName)
else:
# `obj` is not a view, make sure recursive bordering is not requested
assert depth <= 0, (
"Recursive bordering is only supported for UIViews or NSViews"
)
layer = viewHelpers.convertToLayer(obj)
setBorder(layer, options.width, color, colorClassName)
lldb.debugger.HandleCommand("caflush")
def nextColorAfterColor(self, color):
assert color in self.colors, "{} is not a supported color".format(color)
return self.colors[(self.colors.index(color) + 1) % len(self.colors)]
class FBRemoveBorderCommand(fb.FBCommand):
def name(self):
return "unborder"
def description(self):
return "Removes border around <viewOrLayer>."
def options(self):
return [
fb.FBCommandArgument(
short="-d",
long="--depth",
arg="depth",
type="int",
default=0,
help="Number of levels of subviews to unborder.",
)
]
def args(self):
return [
fb.FBCommandArgument(
arg="viewOrLayer",
type="UIView/NSView/CALayer *",
help="The view/layer to unborder.",
)
]
def run(self, args, options):
def setUnborder(layer):
fb.evaluateEffect("[%s setBorderWidth:(CGFloat)%s]" % (layer, 0))
obj = args[0]
depth = int(options.depth)
if viewHelpers.isView(obj):
for view, level in viewHelpers.subviewsOfView(obj):
if level > depth:
break
layer = viewHelpers.convertToLayer(view)
setUnborder(layer)
else:
# `obj` is not a view, make sure recursive unbordering is not requested
assert depth <= 0, (
"Recursive unbordering is only supported for UIViews or NSViews"
)
layer = viewHelpers.convertToLayer(obj)
setUnborder(layer)
lldb.debugger.HandleCommand("caflush")
class FBMaskViewCommand(fb.FBCommand):
def name(self):
return "mask"
def description(self):
return "Add a transparent rectangle to the window to reveal a possibly obscured or hidden view or layer's bounds"
def args(self):
return [
fb.FBCommandArgument(
arg="viewOrLayer",
type="UIView/NSView/CALayer *",
help="The view/layer to mask.",
)
]
def options(self):
return [
fb.FBCommandArgument(
short="-c",
long="--color",
arg="color",
type="string",
default="red",
help="A color name such as 'red', 'green', 'magenta', etc.",
),
fb.FBCommandArgument(
short="-a",
long="--alpha",
arg="alpha",
type="CGFloat",
default=0.5,
help="Desired alpha of mask.",
),
]
def run(self, args, options):
viewOrLayer = fb.evaluateObjectExpression(args[0])
viewHelpers.maskView(viewOrLayer, options.color, options.alpha)
class FBUnmaskViewCommand(fb.FBCommand):
def name(self):
return "unmask"
def description(self):
return "Remove mask from a view or layer"
def args(self):
return [
fb.FBCommandArgument(
arg="viewOrLayer",
type="UIView/CALayer *",
help="The view/layer to mask.",
)
]
def run(self, args, options):
viewOrLayer = fb.evaluateObjectExpression(args[0])
viewHelpers.unmaskView(viewOrLayer)
class FBCoreAnimationFlushCommand(fb.FBCommand):
def name(self):
return "caflush"
def description(self):
return "Force Core Animation to flush. This will 'repaint' the UI but also may mess with ongoing animations."
def run(self, arguments, options):
viewHelpers.flushCoreAnimationTransaction()
class FBShowViewCommand(fb.FBCommand):
def name(self):
return "show"
def description(self):
return "Show a view or layer."
def args(self):
return [
fb.FBCommandArgument(
arg="viewOrLayer",
type="UIView/NSView/CALayer *",
help="The view/layer to show.",
)
]
def run(self, args, options):
viewHelpers.setViewHidden(args[0], False)
class FBHideViewCommand(fb.FBCommand):
def name(self):
return "hide"
def description(self):
return "Hide a view or layer."
def args(self):
return [
fb.FBCommandArgument(
arg="viewOrLayer",
type="UIView/NSView/CALayer *",
help="The view/layer to hide.",
)
]
def run(self, args, options):
viewHelpers.setViewHidden(args[0], True)
class FBPresentViewControllerCommand(fb.FBCommand):
def name(self):
return "present"
def description(self):
return "Present a view controller."
def args(self):
return [
fb.FBCommandArgument(
arg="viewController",
type="UIViewController *",
help="The view controller to present.",
)
]
def run(self, args, option):
viewControllerHelpers.presentViewController(args[0])
class FBDismissViewControllerCommand(fb.FBCommand):
def name(self):
return "dismiss"
def description(self):
return "Dismiss a presented view controller."
def args(self):
return [
fb.FBCommandArgument(
arg="viewController",
type="UIViewController *",
help="The view controller to dismiss.",
)
]
def run(self, args, option):
viewControllerHelpers.dismissViewController(args[0])
class FBSlowAnimationCommand(fb.FBCommand):
def name(self):
return "slowanim"
def description(self):
return "Slows down animations. Works on the iOS Simulator and a device."
def args(self):
return [
fb.FBCommandArgument(
arg="speed",
type="float",
default=0.1,
help="Animation speed (default 0.1).",
)
]
def run(self, args, option):
viewHelpers.slowAnimation(args[0])
class FBUnslowAnimationCommand(fb.FBCommand):
def name(self):
return "unslowanim"
def description(self):
return "Turn off slow animations."
def run(self, args, option):
viewHelpers.slowAnimation()
================================================
FILE: commands/FBFindCommands.py
================================================
#!/usr/bin/python
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
import os
import re
import fbchisellldbbase as fb
import fbchisellldbobjcruntimehelpers as objc
import fbchisellldbviewcontrollerhelpers as vcHelpers
import lldb
def lldbcommands():
return [FBFindViewControllerCommand(), FBFindViewCommand(), FBTapLoggerCommand()]
class FBFindViewControllerCommand(fb.FBCommand):
def name(self):
return "fvc"
def description(self):
return "Find the view controllers whose class names match classNameRegex and puts the address of first on the clipboard."
def options(self):
return [
fb.FBCommandArgument(
short="-n",
long="--name",
arg="classNameRegex",
type="string",
help="The view-controller-class regex to search the view controller hierarchy for.",
),
fb.FBCommandArgument(
short="-v",
long="--view",
arg="view",
type="UIView",
help="This function will print the View Controller that owns this view.",
),
]
def run(self, arguments, options):
if options.classNameRegex and options.view:
print("Do not set both the --name and --view flags")
elif options.view:
self.findOwningViewController(options.view)
else:
output = vcHelpers.viewControllerRecursiveDescription(
"(id)[[[UIApplication sharedApplication] keyWindow] rootViewController]"
)
searchString = (
options.classNameRegex if options.classNameRegex else arguments[0]
)
printMatchesInViewOutputStringAndCopyFirstToClipboard(searchString, output)
def findOwningViewController(self, object):
while object:
if self.isViewController(object):
description = fb.evaluateExpressionValue(object).GetObjectDescription()
print("Found the owning view controller.\n{}".format(description))
cmd = 'echo {} | tr -d "\n" | pbcopy'.format(object)
os.system(cmd)
return
else:
object = self.nextResponder(object)
print("Could not find an owning view controller")
@staticmethod
def isViewController(object):
command = "[(id){} isKindOfClass:[UIViewController class]]".format(object)
isVC = fb.evaluateBooleanExpression(command)
return isVC
@staticmethod
def nextResponder(object):
command = "[((id){}) nextResponder]".format(object)
nextResponder = fb.evaluateObjectExpression(command)
try:
if int(nextResponder, 0):
return nextResponder
else:
return None
except Exception:
return None
class FBFindViewCommand(fb.FBCommand):
def name(self):
return "fv"
def description(self):
return "Find the views whose class names match classNameRegex and puts the address of first on the clipboard."
def args(self):
return [
fb.FBCommandArgument(
arg="classNameRegex",
type="string",
help="The view-class regex to search the view hierarchy for.",
)
]
def run(self, arguments, options):
output = fb.evaluateExpressionValue(
"(id)[[[UIApplication sharedApplication] keyWindow] recursiveDescription]"
).GetObjectDescription()
printMatchesInViewOutputStringAndCopyFirstToClipboard(arguments[0], output)
def printMatchesInViewOutputStringAndCopyFirstToClipboard(needle, haystack):
first = None
for match in re.finditer(
".*<.*(" + needle + ")\\S*: (0x[0-9a-fA-F]*);.*", haystack, re.IGNORECASE
):
view = match.groups()[-1]
className = fb.evaluateExpressionValue(
"(id)[(" + view + ") class]"
).GetObjectDescription()
print("{} {}".format(view, className))
if first is None:
first = view
cmd = 'echo %s | tr -d "\n" | pbcopy' % view
os.system(cmd)
class FBTapLoggerCommand(fb.FBCommand):
def name(self):
return "taplog"
def description(self):
return "Log tapped view to the console."
def run(self, arguments, options):
parameterExpr = objc.functionPreambleExpressionForObjectParameterAtIndex(0)
breakpoint = lldb.debugger.GetSelectedTarget().BreakpointCreateByName(
"-[UIApplication sendEvent:]"
)
breakpoint.SetCondition(
"(int)["
+ parameterExpr
+ " type] == 0 && (int)[[["
+ parameterExpr
+ " allTouches] anyObject] phase] == 0"
)
breakpoint.SetOneShot(True)
callback_name = taplog_callback.__qualname__
# Import the callback so LLDB can see it
lldb.debugger.HandleCommand(
"script from %s import %s" % (__name__, callback_name)
)
breakpoint.SetScriptCallbackFunction(callback_name)
lldb.debugger.SetAsync(True)
lldb.debugger.HandleCommand("continue")
def taplog_callback(frame, bp_loc, internal_dict):
parameterExpr = objc.functionPreambleExpressionForObjectParameterAtIndex(0)
print(
"Gesture Recognizers:\n{}".format(
fb.describeObject(
"[[[%s allTouches] anyObject] gestureRecognizers]" % (parameterExpr)
)
)
)
print(
"View:\n{}".format(
fb.describeObject("[[[%s allTouches] anyObject] view]" % (parameterExpr))
)
)
# We don't want to proceed event (click on button for example), so we just skip it
lldb.debugger.HandleCommand("thread return")
================================================
FILE: commands/FBFlickerCommands.py
================================================
#!/usr/bin/python
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
import os
import sys
import fbchisellldbbase as fb
import fbchisellldbobjcruntimehelpers as runtimeHelpers
import fbchisellldbviewhelpers as viewHelpers
import lldb
def lldbcommands():
return [FBFlickerViewCommand(), FBViewSearchCommand()]
class FBFlickerViewCommand(fb.FBCommand):
def name(self):
return "flicker"
def description(self):
return "Quickly show and hide a view to quickly help visualize where it is."
def args(self):
return [
fb.FBCommandArgument(
arg="viewOrLayer", type="UIView/NSView*", help="The view to flicker."
)
]
def run(self, arguments, options):
object = fb.evaluateObjectExpression(arguments[0])
isHidden = fb.evaluateBooleanExpression("[" + object + " isHidden]")
shouldHide = not isHidden
for _ in range(0, 2):
viewHelpers.setViewHidden(object, shouldHide)
viewHelpers.setViewHidden(object, isHidden)
class FBViewSearchCommand(fb.FBCommand):
def name(self):
return "vs"
def description(self):
return "Interactively search for a view by walking the hierarchy."
def args(self):
return [
fb.FBCommandArgument(arg="view", type="UIView*", help="The view to border.")
]
def run(self, arguments, options):
print(
"\nUse the following and (q) to quit.\n(w) move to superview\n(s) move to first subview\n(a) move to previous sibling\n(d) move to next sibling\n(p) print the hierarchy\n"
)
object = fb.evaluateInputExpression(arguments[0])
walker = FlickerWalker(object)
walker.run()
class FlickerWalker:
def __init__(self, startView):
self.setCurrentView(startView)
def run(self):
self.keepRunning = True
initialAsync = lldb.debugger.GetAsync()
# Needed so XCode doesn't hang if tap on Continue while lldb
# is waiting for user input in 'vs' mode
lldb.debugger.SetAsync(True)
while self.keepRunning:
charRead = sys.stdin.readline().rstrip("\n")
self.inputCallback(charRead)
else:
lldb.debugger.SetAsync(initialAsync)
def inputCallback(self, input):
oldView = self.currentView
if input == "q":
cmd = 'echo %s | tr -d "\n" | pbcopy' % oldView
os.system(cmd)
print(
"\nI hope "
+ oldView
+ " was what you were looking for. I put it on your clipboard."
)
viewHelpers.unmaskView(oldView)
self.keepRunning = False
elif input == "w":
v = superviewOfView(self.currentView)
if not v:
print("There is no superview. Where are you trying to go?!")
self.setCurrentView(v, oldView)
elif input == "s":
v = firstSubviewOfView(self.currentView)
if not v:
print("\nThe view has no subviews.\n")
self.setCurrentView(v, oldView)
elif input == "d":
v = nthSiblingOfView(self.currentView, -1)
if v == oldView:
print("\nThere are no sibling views to this view.\n")
self.setCurrentView(v, oldView)
elif input == "a":
v = nthSiblingOfView(self.currentView, 1)
if v == oldView:
print("\nThere are no sibling views to this view.\n")
self.setCurrentView(v, oldView)
elif input == "p":
recursionName = "recursiveDescription"
isMac = runtimeHelpers.isMacintoshArch()
if isMac:
recursionName = "_subtreeDescription"
print(fb.describeObject("[(id){} {}]".format(oldView, recursionName)))
else:
print("\nI really have no idea what you meant by '" + input + "'... =\\\n")
def setCurrentView(self, view, oldView=None):
if view:
self.currentView = view
if oldView:
viewHelpers.unmaskView(oldView)
viewHelpers.maskView(self.currentView, "red", "0.4")
print(fb.describeObject(view))
def superviewOfView(view):
superview = fb.evaluateObjectExpression("[" + view + " superview]")
if int(superview, 16) == 0:
return None
return superview
def subviewsOfView(view):
return fb.evaluateObjectExpression("[" + view + " subviews]")
def firstSubviewOfView(view):
subviews = subviewsOfView(view)
numViews = fb.evaluateIntegerExpression("[(id)" + subviews + " count]")
if numViews == 0:
return None
else:
return fb.evaluateObjectExpression("[" + subviews + " objectAtIndex:0]")
def nthSiblingOfView(view, n):
subviews = subviewsOfView(superviewOfView(view))
numViews = fb.evaluateIntegerExpression("[(id)" + subviews + " count]")
idx = fb.evaluateIntegerExpression(
"[(id)" + subviews + " indexOfObject:" + view + "]"
)
newIdx = idx + n
while newIdx < 0:
newIdx += numViews
newIdx = newIdx % numViews
return fb.evaluateObjectExpression(
"[(id)" + subviews + " objectAtIndex:" + str(newIdx) + "]"
)
================================================
FILE: commands/FBImportCommands.py
================================================
#!/usr/bin/python
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
import fbchisellldbbase as fb
import lldb
def lldbcommands():
return [ImportUIKitModule()]
class ImportUIKitModule(fb.FBCommand):
def name(self):
return "uikit"
def description(self):
return "Imports the UIKit module to get access to the types while in lldb."
def run(self, arguments, options):
frame = (
lldb.debugger.GetSelectedTarget()
.GetProcess()
.GetSelectedThread()
.GetSelectedFrame()
)
fb.importModule(frame, "UIKit")
================================================
FILE: commands/FBInvocationCommands.py
================================================
#!/usr/bin/python
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
import re
import fbchisellldbbase as fb
import lldb
def lldbcommands():
return [FBPrintInvocation()]
class FBPrintInvocation(fb.FBCommand):
def name(self):
return "pinvocation"
def description(self):
return "Print the stack frame, receiver, and arguments of the current invocation. It will fail to print all arguments if any arguments are variadic (varargs).\n\nNOTE: Sadly this is currently only implemented on x86."
def options(self):
return [
fb.FBCommandArgument(
short="-a",
long="--all",
arg="all",
default=False,
boolean=True,
help="Specify to print the entire stack instead of just the current frame.",
)
]
def run(self, arguments, options):
target = lldb.debugger.GetSelectedTarget()
if not re.match(r".*i386.*", target.GetTriple()):
print("Only x86 is currently supported (32-bit iOS Simulator or Mac OS X).")
return
thread = target.GetProcess().GetSelectedThread()
if options.all:
for frame in thread:
printInvocationForFrame(frame)
print("---------------------------------")
else:
frame = thread.GetSelectedFrame()
printInvocationForFrame(frame)
def printInvocationForFrame(frame):
print(frame)
symbolName = frame.GetSymbol().GetName()
if not re.match(r"[-+]\s*\[.*\]", symbolName):
return
self = findArgAtIndexFromStackFrame(frame, 0)
cmd = findArgAtIndexFromStackFrame(frame, 1)
commandForSignature = (
"[(id)"
+ self
+ " methodSignatureForSelector:(char *)sel_getName((SEL)"
+ cmd
+ ")]"
)
signatureValue = fb.evaluateExpressionValue("(id)" + commandForSignature)
if (
signatureValue.GetError() is not None
and str(signatureValue.GetError()) != "success"
):
print(
"My sincerest apologies. I couldn't find a method signature for the selector."
)
return
signature = signatureValue.GetValue()
arg0 = stackStartAddressInSelectedFrame(frame)
commandForInvocation = (
"[NSInvocation _invocationWithMethodSignature:(id)"
+ signature
+ " frame:((void *)"
+ str(arg0)
+ ")]"
)
invocation = fb.evaluateExpression("(id)" + commandForInvocation)
if invocation:
prettyPrintInvocation(frame, invocation)
else:
print(frame)
def stackStartAddressInSelectedFrame(frame):
# Determine if the %ebp register has already had the
# stack register pushed into it (always the first instruction)
frameSymbol = frame.GetSymbolContext(0).GetSymbol()
frameStartAddress = frameSymbol.GetStartAddress().GetLoadAddress(
lldb.debugger.GetSelectedTarget()
)
currentPC = frame.GetPC()
offset = currentPC - frameStartAddress
if offset == 0:
return int(frame.EvaluateExpression("($esp + 4)").GetValue())
elif offset == 1:
return int(frame.EvaluateExpression("($esp + 8)").GetValue())
else:
return int(frame.EvaluateExpression("($ebp + 8)").GetValue())
def findArgAtIndexFromStackFrame(frame, index):
return fb.evaluateExpression(
"*(int *)" + str(findArgAdressAtIndexFromStackFrame(frame, index))
)
def findArgAdressAtIndexFromStackFrame(frame, index):
arg0 = stackStartAddressInSelectedFrame(frame)
arg = arg0 + 4 * index
return arg
def prettyPrintInvocation(frame, invocation):
object = fb.evaluateExpression("(id)[(id)" + invocation + " target]")
description = fb.evaluateExpressionValue("(id)" + invocation).GetObjectDescription()
argDescriptions = description.splitlines(True)[4:]
print("NSInvocation: " + invocation)
print("self: " + fb.evaluateExpression("(id)" + object))
if len(argDescriptions) > 0:
print(
"\n" + str(len(argDescriptions)) + " Arguments:"
if len(argDescriptions) > 1
else "\nArgument:"
)
index = 2
for argDescription in argDescriptions:
s = re.sub(r"argument [0-9]+: ", "", argDescription)
address = findArgAdressAtIndexFromStackFrame(frame, index)
encoding = s.split(" ")[0]
description = " ".join(s.split(" ")[1:])
readableString = argumentAsString(frame, address, encoding)
if readableString:
print(readableString)
else:
if encoding[0] == "{":
encoding = encoding[1:]
print(
(
hex(address) + ", address of " + encoding + " " + description
).strip()
)
index += 1
def argumentAsString(frame, address, encoding): # noqa C901
if encoding[0] == "{":
encoding = encoding[1:]
encodingMap = {
"c": "char",
"i": "int",
"s": "short",
"l": "long",
"q": "long long",
"C": "unsigned char",
"I": "unsigned int",
"S": "unsigned short",
"L": "unsigned long",
"Q": "unsigned long long",
"f": "float",
"d": "double",
"B": "bool",
"v": "void",
"*": "char *",
"@": "id",
"#": "Class",
":": "SEL",
}
pointers = ""
while encoding[0] == "^":
pointers += "*"
encoding = encoding[1:]
type = None
if encoding in encodingMap:
type = encodingMap[encoding]
if type and pointers:
type = type + " " + pointers
if not type:
# Handle simple structs: {CGPoint=ff}, {CGSize=ff},
# {CGRect={CGPoint=ff}{CGSize=ff}}
if encoding[0] == "{":
encoding = encoding[1:]
type = re.sub(r"=.*", "", encoding)
if pointers:
type += " " + pointers
if type:
value = frame.EvaluateExpression("*(" + type + " *)" + str(address))
if value.GetError() is None or str(value.GetError()) == "success":
description = None
if encoding == "@":
description = value.GetObjectDescription()
if not description:
description = value.GetValue()
if not description:
description = value.GetSummary()
if description:
return type + ": " + description
return None
================================================
FILE: commands/FBPrintCommands.py
================================================
#!/usr/bin/python
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
import os
import re
import subprocess
import fbchisellldbbase as fb
import fbchisellldbobjcruntimehelpers as runtimeHelpers
import fbchisellldbviewcontrollerhelpers as vcHelpers
import fbchisellldbviewhelpers as viewHelpers
import lldb
def lldbcommands():
return [
FBPrintViewHierarchyCommand(),
FBPrintViewControllerHierarchyCommand(),
FBPrintIsExecutingInAnimationBlockCommand(),
FBPrintInheritanceHierarchy(),
FBPrintUpwardResponderChain(),
FBPrintOnscreenTableView(),
FBPrintOnscreenTableViewCells(),
FBPrintInternals(),
FBPrintInstanceVariable(),
FBPrintKeyPath(),
FBPrintApplicationDocumentsPath(),
FBPrintApplicationBundlePath(),
FBPrintData(),
FBPrintTargetActions(),
FBPrintJSON(),
FBPrintSwiftJSON(),
FBPrintAsCurl(),
FBPrintToClipboard(),
FBPrintObjectInObjc(),
]
class FBPrintViewHierarchyCommand(fb.FBCommand):
def name(self):
return "pviews"
def description(self):
return "Print the recursion description of <aView>."
def options(self):
return [
fb.FBCommandArgument(
short="-u",
long="--up",
arg="upwards",
boolean=True,
default=False,
help="Print only the hierarchy directly above the view, up to its window.",
),
fb.FBCommandArgument(
short="-d",
long="--depth",
arg="depth",
type="int",
default="0",
help="Print only to a given depth. 0 indicates infinite depth.",
),
fb.FBCommandArgument(
short="-w",
long="--window",
arg="window",
type="int",
default="0",
help='Specify the window to print a description of. Check which windows exist with "po (id)[[UIApplication sharedApplication] windows]".',
),
fb.FBCommandArgument(
short="-s",
long="--short",
arg="short",
boolean=True,
default=False,
help="Print a short description of the view",
),
fb.FBCommandArgument(
short="-m",
long="--medium",
arg="medium",
boolean=True,
default=False,
help="Print a medium description of the view",
),
]
def args(self):
return [
fb.FBCommandArgument(
arg="aView",
type="UIView*/NSView*",
help="The view to print the description of.",
default="__keyWindow_dynamic__",
)
]
def run(self, arguments, options):
maxDepth = int(options.depth)
window = int(options.window)
isMac = runtimeHelpers.isMacintoshArch()
if window > 0:
if isMac:
arguments[0] = (
"(id)[[[[NSApplication sharedApplication] windows] objectAtIndex:"
+ str(window)
+ "] contentView]"
)
else:
arguments[0] = (
"(id)[[[UIApplication sharedApplication] windows] objectAtIndex:"
+ str(window)
+ "]"
)
elif arguments[0] == "__keyWindow_dynamic__":
if isMac:
arguments[0] = (
"(id)[[[[NSApplication sharedApplication] windows] objectAtIndex:0] contentView]"
)
else:
arguments[0] = "(id)[[UIApplication sharedApplication] keyWindow]"
if options.upwards:
view = arguments[0]
description = viewHelpers.upwardsRecursiveDescription(view, maxDepth)
if description:
print(description)
else:
print(
"Failed to walk view hierarchy. Make sure you pass a view, not any other kind of object or expression."
)
else:
printingMethod = "recursiveDescription"
if isMac:
printingMethod = "_subtreeDescription"
description = fb.evaluateExpressionValue(
"(id)[" + arguments[0] + " " + printingMethod + "]"
).GetObjectDescription()
if maxDepth > 0:
separator = re.escape(" | ")
prefixToRemove = separator * maxDepth + " "
description += "\n"
description = re.sub(r"%s.*\n" % (prefixToRemove), r"", description)
if options.short:
toRemove = ":.*(?:\n|$)"
description = re.sub(toRemove, r">\n", description)
elif options.medium:
toRemove = ";.*(?:\n|$)"
description = re.sub(toRemove, r">\n", description)
print(description)
class FBPrintViewControllerHierarchyCommand(fb.FBCommand):
def name(self):
return "pvc"
def description(self):
return "Print the recursion description of <aViewController>."
def args(self):
return [
fb.FBCommandArgument(
arg="aViewController",
type="UIViewController*",
help="The view controller to print the description of.",
default="__keyWindow_rootVC_dynamic__",
)
]
def run(self, arguments, options):
isMac = runtimeHelpers.isMacintoshArch()
if arguments[0] == "__keyWindow_rootVC_dynamic__":
if fb.evaluateBooleanExpression(
"[UIViewController respondsToSelector:@selector(_printHierarchy)]"
):
print(fb.describeObject("[UIViewController _printHierarchy]"))
return
arguments[0] = (
"(id)[(id)[[UIApplication sharedApplication] keyWindow] rootViewController]"
)
if isMac:
arguments[0] = (
"(id)[[[[NSApplication sharedApplication] windows] objectAtIndex:0] contentViewController]"
)
print(vcHelpers.viewControllerRecursiveDescription(arguments[0]))
class FBPrintIsExecutingInAnimationBlockCommand(fb.FBCommand):
def name(self):
return "panim"
def description(self):
return (
"Prints if the code is currently execution with a UIView animation block."
)
def run(self, arguments, options):
lldb.debugger.HandleCommand("p (BOOL)[UIView _isInAnimationBlock]")
def _printIterative(initialValue, generator):
indent = 0
for currentValue in generator(initialValue):
print(" | " * indent + currentValue)
indent += 1
class FBPrintInheritanceHierarchy(fb.FBCommand):
def name(self):
return "pclass"
def description(self):
return "Print the inheritance starting from an instance of any class."
def args(self):
return [
fb.FBCommandArgument(
arg="object", type="id", help="The instance to examine."
)
]
def run(self, arguments, options):
_printIterative(arguments[0], _inheritanceHierarchy)
def _inheritanceHierarchy(instanceOfAClass):
instanceAddress = fb.evaluateExpression(instanceOfAClass)
instanceClass = fb.evaluateExpression("(id)[(id)" + instanceAddress + " class]")
while int(instanceClass, 16):
yield fb.evaluateExpressionValue(instanceClass).GetObjectDescription()
instanceClass = fb.evaluateExpression(
"(id)[(id)" + instanceClass + " superclass]"
)
class FBPrintUpwardResponderChain(fb.FBCommand):
def name(self):
return "presponder"
def description(self):
return "Print the responder chain starting from a specific responder."
def args(self):
return [
fb.FBCommandArgument(
arg="startResponder",
type="UIResponder *",
help="The responder to use to start walking the chain.",
)
]
def run(self, arguments, options):
startResponder = fb.evaluateInputExpression(arguments[0])
isMac = runtimeHelpers.isMacintoshArch()
responderClass = "UIResponder"
if isMac:
responderClass = "NSResponder"
if not fb.evaluateBooleanExpression(
"(BOOL)[(id)"
+ startResponder
+ " isKindOfClass:["
+ responderClass
+ " class]]"
):
print("Whoa, " + startResponder + " is not a " + responderClass + ". =(")
return
_printIterative(startResponder, _responderChain)
def _responderChain(startResponder):
responderAddress = fb.evaluateExpression(startResponder)
while int(responderAddress, 16):
yield fb.evaluateExpressionValue(responderAddress).GetObjectDescription()
responderAddress = fb.evaluateExpression(
"(id)[(id)" + responderAddress + " nextResponder]"
)
def tableViewInHierarchy():
viewDescription = fb.evaluateExpressionValue(
"(id)[(id)[[UIApplication sharedApplication] keyWindow] recursiveDescription]"
).GetObjectDescription()
searchView = None
# Try to find an instance of
classPattern = re.compile(r"UITableView: (0x[0-9a-fA-F]+);")
for match in re.finditer(classPattern, viewDescription):
searchView = match.group(1)
break
# Try to find a direct subclass
if not searchView:
subclassPattern = re.compile(r"(0x[0-9a-fA-F]+); baseClass = UITableView;")
for match in re.finditer(subclassPattern, viewDescription):
searchView = match.group(1)
break
# SLOW: check every pointer in town
if not searchView:
pattern = re.compile(r"(0x[0-9a-fA-F]+)[;>]")
for view in re.findall(pattern, viewDescription):
if fb.evaluateBooleanExpression(
"[" + view + " isKindOfClass:(id)[UITableView class]]"
):
searchView = view
break
return searchView
class FBPrintOnscreenTableView(fb.FBCommand):
def name(self):
return "ptv"
def description(self):
return "Print the highest table view in the hierarchy."
def run(self, arguments, options):
tableView = tableViewInHierarchy()
if tableView:
viewValue = fb.evaluateExpressionValue(tableView)
print(viewValue.GetObjectDescription())
cmd = 'echo %s | tr -d "\n" | pbcopy' % tableView
os.system(cmd)
else:
print("Sorry, chump. I couldn't find a table-view. :'(")
class FBPrintOnscreenTableViewCells(fb.FBCommand):
def name(self):
return "pcells"
def description(self):
return "Print the visible cells of the highest table view in the hierarchy."
def run(self, arguments, options):
tableView = tableViewInHierarchy()
print(
fb.evaluateExpressionValue(
"(id)[(id)" + tableView + " visibleCells]"
).GetObjectDescription()
)
class FBPrintInternals(fb.FBCommand):
def name(self):
return "pinternals"
def description(self):
return "Show the internals of an object by dereferencing it as a pointer."
def args(self):
return [
fb.FBCommandArgument(
arg="object", type="id", help="Object expression to be evaluated."
)
]
def options(self):
return [
fb.FBCommandArgument(
arg="appleWay",
short="-a",
long="--apple",
boolean=True,
default=False,
help="Print ivars the apple way",
)
]
def run(self, arguments, options):
object = fb.evaluateObjectExpression(arguments[0])
if options.appleWay:
if fb.evaluateBooleanExpression(
"[{} respondsToSelector:@selector(_ivarDescription)]".format(object)
):
command = "po [{} _ivarDescription]".format(object)
else:
print("Sorry, but it seems Apple dumped the _ivarDescription method")
return
else:
objectClass = fb.evaluateExpressionValue(
"(id)[(id)(" + object + ") class]"
).GetObjectDescription()
command = "p *(({} *)((id){}))".format(objectClass, object)
lldb.debugger.HandleCommand(command)
class FBPrintInstanceVariable(fb.FBCommand):
def name(self):
return "pivar"
def description(self):
return "Print the value of an object's named instance variable."
def args(self):
return [
fb.FBCommandArgument(
arg="object", type="id", help="Object expression to be evaluated."
),
fb.FBCommandArgument(
arg="ivarName", help="Name of instance variable to print."
),
]
def run(self, arguments, options):
object = fb.evaluateInputExpression(arguments[0])
ivarName = arguments[1]
objectClass = fb.evaluateExpressionValue(
"(id)[(" + object + ") class]"
).GetObjectDescription()
ivarTypeCommand = '((char *)ivar_getTypeEncoding((void*)object_getInstanceVariable((id){}, "{}", 0)))[0]'.format(
object, ivarName
)
ivarTypeEncodingFirstChar = fb.evaluateExpression(ivarTypeCommand)
result = fb.evaluateExpressionValue(
"(({} *)({}))->{}".format(objectClass, object, ivarName)
)
print(
result.GetObjectDescription()
if "@" in ivarTypeEncodingFirstChar
else result
)
class FBPrintKeyPath(fb.FBCommand):
def name(self):
return "pkp"
def description(self):
return "Print out the value of the key path expression using -valueForKeyPath:"
def args(self):
return [
fb.FBCommandArgument(
arg="keypath", type="NSString *", help="The keypath to print"
)
]
def run(self, arguments, options):
command = arguments[0]
if len(command.split(".")) == 1:
lldb.debugger.HandleCommand("po " + command)
else:
objectToMessage, keypath = command.split(".", 1)
object = fb.evaluateObjectExpression(objectToMessage)
print(
fb.describeObject('[{} valueForKeyPath:@"{}"]'.format(object, keypath))
)
class FBPrintApplicationDocumentsPath(fb.FBCommand):
def name(self):
return "pdocspath"
def description(self):
return "Print application's 'Documents' directory path."
def options(self):
return [
fb.FBCommandArgument(
short="-o",
long="--open",
arg="open",
boolean=True,
default=False,
help="open in Finder",
)
]
def run(self, arguments, options):
# in iOS SDK NSDocumentDirectory == 9 NSUserDomainMask == 1
NSDocumentDirectory = "9"
NSUserDomainMask = "1"
path = fb.evaluateExpressionValue(
"(NSString*)[NSSearchPathForDirectoriesInDomains("
+ NSDocumentDirectory
+ ", "
+ NSUserDomainMask
+ ", YES) lastObject]"
)
pathString = "{}".format(path).split('"')[1]
cmd = 'echo {} | tr -d "\n" | pbcopy'.format(pathString)
os.system(cmd)
print(pathString)
if options.open:
os.system("open " + pathString)
class FBPrintApplicationBundlePath(fb.FBCommand):
def name(self):
return "pbundlepath"
def description(self):
return "Print application's bundle directory path."
def options(self):
return [
fb.FBCommandArgument(
short="-o",
long="--open",
arg="open",
boolean=True,
default=False,
help="open in Finder",
)
]
def run(self, arguments, options):
path = fb.evaluateExpressionValue(
"(NSString*)[[NSBundle mainBundle] bundlePath]"
)
pathString = "{}".format(path).split('"')[1]
cmd = 'echo {} | tr -d "\n" | pbcopy'.format(pathString)
os.system(cmd)
print(pathString)
if options.open:
os.system("open " + pathString)
class FBPrintData(fb.FBCommand):
def name(self):
return "pdata"
def description(self):
return (
"Print the contents of NSData object as string.\n"
"Supported encodings:\n"
"- ascii,\n"
"- utf8,\n"
"- utf16, unicode,\n"
"- utf16l (Little endian),\n"
"- utf16b (Big endian),\n"
"- utf32,\n"
"- utf32l (Little endian),\n"
"- utf32b (Big endian),\n"
"- latin1, iso88591 (88591),\n"
"- latin2, iso88592 (88592),\n"
"- cp1251 (1251),\n"
"- cp1252 (1252),\n"
"- cp1253 (1253),\n"
"- cp1254 (1254),\n"
"- cp1250 (1250),"
)
def options(self):
return [
fb.FBCommandArgument(
arg="encoding",
short="-e",
long="--encoding",
type="string",
help="Used encoding (default utf-8).",
default="utf-8",
)
]
def args(self):
return [
fb.FBCommandArgument(arg="data", type="NSData *", help="NSData object.")
]
def run(self, arguments, option): # noqa C901
# Normalize encoding.
encoding_text = option.encoding.lower().replace(" -", "")
enc = 4 # Default encoding UTF-8.
if encoding_text == "ascii":
enc = 1
elif encoding_text == "utf8":
enc = 4
elif (
encoding_text == "latin1"
or encoding_text == "88591"
or encoding_text == "iso88591"
):
enc = 5
elif (
encoding_text == "latin2"
or encoding_text == "88592"
or encoding_text == "iso88592"
):
enc = 9
elif encoding_text == "unicode" or encoding_text == "utf16":
enc = 10
elif encoding_text == "1251" or encoding_text == "cp1251":
enc = 11
elif encoding_text == "1252" or encoding_text == "cp1252":
enc = 12
elif encoding_text == "1253" or encoding_text == "cp1253":
enc = 13
elif encoding_text == "1254" or encoding_text == "cp1254":
enc = 14
elif encoding_text == "1250" or encoding_text == "cp1250":
enc = 15
elif encoding_text == "utf16b":
enc = 0x90000100
elif encoding_text == "utf16l":
enc = 0x94000100
elif encoding_text == "utf32":
enc = 0x8C000100
elif encoding_text == "utf32b":
enc = 0x98000100
elif encoding_text == "utf32l":
enc = 0x9C000100
print(
fb.describeObject(
"[[NSString alloc] initWithData:{} encoding:{}]".format(
arguments[0], enc
)
)
)
class FBPrintTargetActions(fb.FBCommand):
def name(self):
return "pactions"
def description(self):
return "Print the actions and targets of a control."
def args(self):
return [
fb.FBCommandArgument(
arg="control",
type="UIControl *",
help="The control to inspect the actions of.",
)
]
def run(self, arguments, options):
control = fb.evaluateInputExpression(arguments[0])
targets = fb.evaluateObjectExpression(
"[[{control} allTargets] allObjects]".format(control=control)
)
targetCount = fb.evaluateIntegerExpression(
"[{targets} count]".format(targets=targets)
)
for index in range(0, targetCount):
target = fb.evaluateObjectExpression(
"[{targets} objectAtIndex:{index}]".format(targets=targets, index=index)
)
actions = fb.evaluateObjectExpression(
"[{control} actionsForTarget:{target} forControlEvent:0]".format(
control=control, target=target
)
)
targetDescription = fb.evaluateExpressionValue(
"(id){target}".format(target=target)
).GetObjectDescription()
actionsDescription = fb.evaluateExpressionValue(
'(id)[{actions} componentsJoinedByString:@", "]'.format(actions=actions)
).GetObjectDescription()
print(
"{target}: {actions}".format(
target=targetDescription, actions=actionsDescription
)
)
class FBPrintJSON(fb.FBCommand):
def name(self):
return "pjson"
def description(self):
return "Print JSON representation of NSDictionary or NSArray object"
def options(self):
return [
fb.FBCommandArgument(
arg="plain",
short="-p",
long="--plain",
boolean=True,
default=False,
help="Plain JSON",
)
]
def args(self):
return [
fb.FBCommandArgument(
arg="object",
type="id",
help="The NSDictionary or NSArray object to print",
)
]
def run(self, arguments, options):
objectToPrint = fb.evaluateInputExpression(arguments[0])
pretty = 1 if options.plain is None else 0
jsonData = fb.evaluateObjectExpression(
"[NSJSONSerialization dataWithJSONObject:(id){} options:{} error:nil]".format(
objectToPrint, pretty
)
)
jsonString = fb.evaluateExpressionValue(
"(NSString*)[[NSString alloc] initWithData:(id){} encoding:4]".format(
jsonData
)
).GetObjectDescription()
print(jsonString)
class FBPrintSwiftJSON(fb.FBCommand):
def name(self):
return "psjson"
def description(self):
return "Print JSON representation of Swift Dictionary or Swift Array object"
def options(self):
return [
fb.FBCommandArgument(
arg="plain",
short="-p",
long="--plain",
boolean=True,
default=False,
help="Plain JSON",
)
]
def args(self):
return [
fb.FBCommandArgument(
arg="object",
type="NSObject *",
help="The Swift Dictionary or Swift Array to print",
)
]
def run(self, arguments, options):
# Convert to NSObject first to allow for objc runtime to process it
objectToPrint = fb.evaluateInputExpression(
"{obj} as NSObject".format(obj=arguments[0])
)
pretty = 1 if options.plain is None else 0
jsonData = fb.evaluateObjectExpression(
"[NSJSONSerialization dataWithJSONObject:(NSObject*){} options:{} error:nil]".format(
objectToPrint, pretty
)
)
jsonString = fb.evaluateExpressionValue(
"(NSString*)[[NSString alloc] initWithData:(NSObject*){} encoding:4]".format(
jsonData
)
).GetObjectDescription()
print(jsonString)
class FBPrintAsCurl(fb.FBCommand):
def name(self):
return "pcurl"
def description(self):
return "Print the NSURLRequest (HTTP) as curl command."
def options(self):
return [
fb.FBCommandArgument(
short="-e",
long="--embed-data",
arg="embed",
boolean=True,
default=False,
help="Embed request data as base64.",
)
]
def args(self):
return [
fb.FBCommandArgument(
arg="request",
type="NSURLRequest*/NSMutableURLRequest*",
help="The request to convert to the curl command.",
)
]
def generateTmpFilePath(self):
return "/tmp/curl_data_{}".format(
fb.evaluateExpression(
"(NSTimeInterval)[NSDate timeIntervalSinceReferenceDate]"
)
)
def run(self, arguments, options):
request = fb.evaluateInputExpression(arguments[0])
HTTPHeaderSring = ""
HTTPMethod = fb.evaluateExpressionValue(
"(id)[{} HTTPMethod]".format(request)
).GetObjectDescription()
URL = fb.evaluateExpressionValue(
"(id)[{} URL]".format(request)
).GetObjectDescription()
timeout = fb.evaluateExpression(
"(NSTimeInterval)[{} timeoutInterval]".format(request)
)
HTTPHeaders = fb.evaluateObjectExpression(
"(id)[{} allHTTPHeaderFields]".format(request)
)
HTTPHeadersCount = fb.evaluateIntegerExpression(
"[{} count]".format(HTTPHeaders)
)
allHTTPKeys = fb.evaluateObjectExpression("[{} allKeys]".format(HTTPHeaders))
for index in range(0, HTTPHeadersCount):
key = fb.evaluateObjectExpression(
"[{} objectAtIndex:{}]".format(allHTTPKeys, index)
)
keyDescription = fb.evaluateExpressionValue(
"(id){}".format(key)
).GetObjectDescription()
value = fb.evaluateExpressionValue(
"(id)[(id){} objectForKey:{}]".format(HTTPHeaders, key)
).GetObjectDescription()
if len(HTTPHeaderSring) > 0:
HTTPHeaderSring += " "
HTTPHeaderSring += '-H "{}: {}"'.format(keyDescription, value)
HTTPData = fb.evaluateObjectExpression("[{} HTTPBody]".format(request))
dataFile = None
dataAsString = None
if fb.evaluateIntegerExpression("[{} length]".format(HTTPData)) > 0:
if options.embed:
if fb.evaluateIntegerExpression(
"[{} respondsToSelector:@selector(base64EncodedStringWithOptions:)]".format(
HTTPData
)
):
dataAsString = fb.evaluateExpressionValue(
"(id)[(id){} base64EncodedStringWithOptions:0]".format(HTTPData)
).GetObjectDescription()
else:
print("This version of OS doesn't supports base64 data encoding")
return False
elif not runtimeHelpers.isIOSDevice():
dataFile = self.generateTmpFilePath()
if not fb.evaluateBooleanExpression(
'(BOOL)[{} writeToFile:@"{}" atomically:NO]'.format(
HTTPData, dataFile
)
):
print("Can't write data to file {}".format(dataFile))
return False
else:
print(
'HTTPBody data for iOS Device is supported only with "--embed-data" flag'
)
return False
commandString = ""
if dataAsString is not None and len(dataAsString) > 0:
dataFile = self.generateTmpFilePath()
commandString += 'echo "{}" | base64 -D -o "{}" && '.format(
dataAsString, dataFile
)
commandString += "curl -X {} --connect-timeout {}".format(HTTPMethod, timeout)
if len(HTTPHeaderSring) > 0:
commandString += " " + HTTPHeaderSring
if dataFile is not None:
commandString += ' --data-binary @"{}"'.format(dataFile)
commandString += ' "{}"'.format(URL)
print(commandString)
class FBPrintToClipboard(fb.FBCommand):
def name(self):
return "pbcopy"
def description(self):
return "Print object and copy output to clipboard"
def args(self):
return [
fb.FBCommandArgument(arg="object", type="id", help="The object to print")
]
def run(self, arguments, options):
lldbOutput = fb.evaluateExpressionValue(
"[{changeset} description]".format(changeset=arguments[0])
).GetObjectDescription()
process = subprocess.Popen(
"pbcopy", env={"LANG": "en_US.UTF-8"}, stdin=subprocess.PIPE
)
process.communicate(lldbOutput.encode("utf-8"))
print("Object copied to clipboard")
class FBPrintObjectInObjc(fb.FBCommand):
def name(self):
return "poobjc"
def description(self):
return 'Print the expression result, with the expression run in an ObjC++ context. (Shortcut for "expression -O -l ObjC++ -- " )'
def args(self):
return [
fb.FBCommandArgument(
arg="expression", help="ObjC expression to evaluate and print."
)
]
def run(self, arguments, options):
expression = arguments[0]
lldb.debugger.HandleCommand("expression -O -l ObjC++ -- " + expression)
================================================
FILE: commands/FBTextInputCommands.py
================================================
#!/usr/bin/python
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
import fbchisellldbbase as fb
import fbchisellldbviewhelpers as viewHelpers
ACCESSIBILITY_ID = 0
REPLACEMENT_TEXT = 1
INPUT_TEXT = 0
def lldbcommands():
return [FBInputTexByAccessibilityIdCommand(), FBInputTexToFirstResponderCommand()]
class FBInputTexByAccessibilityIdCommand(fb.FBCommand):
def name(self):
return "settext"
def description(self):
return "Set text on a view by accessibility id."
def args(self):
return [
fb.FBCommandArgument(
arg="accessibilityId",
type="string",
help="The accessibility ID of the input view.",
),
fb.FBCommandArgument(
arg="replacementText", type="string", help="The text to set."
),
]
def run(self, arguments, options):
self.findView(
rootView(), arguments[ACCESSIBILITY_ID], arguments[REPLACEMENT_TEXT]
)
def findView(self, view, searchIdentifier, replacementText):
views = subviewsOfView(view)
for index in range(0, viewsCount(views)):
subview = subviewAtIndex(views, index)
self.findView(subview, searchIdentifier, replacementText)
else:
identifier = accessibilityIdentifier(view)
if isEqualToString(identifier, searchIdentifier):
setTextInView(view, replacementText)
class FBInputTexToFirstResponderCommand(fb.FBCommand):
def name(self):
return "setinput"
def description(self):
return "Input text into text field or text view that is first responder."
def args(self):
return [
fb.FBCommandArgument(
arg="inputText", type="string", help="The text to input."
)
]
def run(self, arguments, options):
self.findFirstResponder(rootView(), arguments[INPUT_TEXT])
def findFirstResponder(self, view, replacementText):
views = subviewsOfView(view)
if isFirstResponder(view):
setTextInView(view, replacementText)
else:
for index in range(0, viewsCount(views)):
subview = subviewAtIndex(views, index)
self.findFirstResponder(subview, replacementText)
# Some helpers
def rootView():
return fb.evaluateObjectExpression("[[UIApplication sharedApplication] keyWindow]")
def subviewsOfView(view):
return fb.evaluateObjectExpression("[%s subviews]" % view)
def subviewAtIndex(views, index):
return fb.evaluateObjectExpression("[%s objectAtIndex:%i]" % (views, index))
def viewsCount(views):
return int(fb.evaluateExpression("(int)[%s count]" % views))
def accessibilityIdentifier(view):
return fb.evaluateObjectExpression("[%s accessibilityIdentifier]" % view)
def isEqualToString(identifier, needle):
return fb.evaluateBooleanExpression(
'[%s isEqualToString:@"%s"]' % (identifier, needle)
)
def setTextInView(view, text):
fb.evaluateObjectExpression('[%s setText:@"%s"]' % (view, text))
viewHelpers.flushCoreAnimationTransaction()
def isFirstResponder(view):
return fb.evaluateBooleanExpression("[%s isFirstResponder]" % view)
================================================
FILE: commands/FBVisualizationCommands.py
================================================
#!/usr/bin/python
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
import errno
import os
import time
import fbchisellldbbase as fb
import fbchisellldbobjecthelpers as objectHelpers
import lldb
def lldbcommands():
return [FBVisualizeCommand()]
def _showImage(commandForImage):
imageDirectory = "/tmp/xcode_debug_images/"
imageName = time.strftime("%Y-%m-%d-%H-%M-%S", time.gmtime()) + ".png"
imagePath = imageDirectory + imageName
try:
os.makedirs(imageDirectory)
except OSError as e:
if e.errno == errno.EEXIST and os.path.isdir(imageDirectory):
pass
else:
raise
toPNG = "(id)UIImagePNGRepresentation((id){})".format(commandForImage)
imageDataAddress = fb.evaluateExpressionValue(toPNG, tryAllThreads=True).GetValue()
imageBytesStartAddress = fb.evaluateExpression(
"(void *)[(id)" + imageDataAddress + " bytes]"
)
imageBytesLength = fb.evaluateExpression(
"(NSUInteger)[(id)" + imageDataAddress + " length]"
)
address = int(imageBytesStartAddress, 16)
length = int(imageBytesLength)
if not (address or length):
print("Could not get image data.")
return
process = lldb.debugger.GetSelectedTarget().GetProcess()
error = lldb.SBError()
mem = process.ReadMemory(address, length, error)
if error is not None and str(error) != "success":
print(error)
else:
with open(imagePath, "wb") as imgFile:
imgFile.write(mem)
os.system("open " + imagePath)
def _colorIsCGColorRef(color):
color = "(CGColorRef)(" + color + ")"
result = fb.evaluateExpressionValue(
"(unsigned long)CFGetTypeID({color}) == (unsigned long)CGColorGetTypeID()".format(
color=color
)
)
if result.GetError() is not None and str(result.GetError()) != "success":
print("got error: {}".format(result))
return False
else:
isCFColor = result.GetValueAsUnsigned() != 0
return isCFColor
def _showColor(color):
color = "(" + color + ")"
colorToUse = color
isCF = _colorIsCGColorRef(color)
if isCF:
colorToUse = "[[UIColor alloc] initWithCGColor:(CGColorRef){}]".format(color)
else:
isCI = objectHelpers.isKindOfClass(color, "CIColor")
if isCI:
colorToUse = "[UIColor colorWithCIColor:(CIColor *){}]".format(color)
imageSize = 58
fb.evaluateEffect(
"UIGraphicsBeginImageContextWithOptions((CGSize)CGSizeMake({imageSize}, {imageSize}), NO, 0.0)".format(
imageSize=imageSize
)
)
fb.evaluateEffect("[(id){} setFill]".format(colorToUse))
fb.evaluateEffect(
"UIRectFill((CGRect)CGRectMake(0.0, 0.0, {imageSize}, {imageSize}))".format(
imageSize=imageSize
)
)
result = fb.evaluateExpressionValue(
"(UIImage *)UIGraphicsGetImageFromCurrentImageContext()"
)
if result.GetError() is not None and str(result.GetError()) != "success":
print("got error {}".format(result))
print(result.GetError())
else:
image = result.GetValue()
_showImage(image)
fb.evaluateEffect("UIGraphicsEndImageContext()")
def _showLayer(layer):
layer = "(" + layer + ")"
size = "((CGRect)[(id)" + layer + " bounds]).size"
width = float(fb.evaluateExpression("(CGFloat)(" + size + ".width)"))
height = float(fb.evaluateExpression("(CGFloat)(" + size + ".height)"))
if width == 0.0 or height == 0.0:
print(
"Nothing to see here - the size of this element is {} x {}.".format(
width, height
)
)
return
fb.evaluateEffect("UIGraphicsBeginImageContextWithOptions(" + size + ", NO, 0.0)")
fb.evaluateEffect(
"[(id)" + layer + " renderInContext:(void *)UIGraphicsGetCurrentContext()]"
)
result = fb.evaluateExpressionValue(
"(UIImage *)UIGraphicsGetImageFromCurrentImageContext()"
)
if result.GetError() is not None and str(result.GetError()) != "success":
print(result.GetError())
else:
image = result.GetValue()
_showImage(image)
fb.evaluateEffect("UIGraphicsEndImageContext()")
def _showPixelBuffer(target):
imgVar = "$imageOut" + str(round(time.time()))
fb.evaluateExpression("CGImageRef " + imgVar + " = NULL")
fb.evaluateExpression(
"(OSStatus)VTCreateCGImageFromCVPixelBuffer((CVPixelBufferRef)"
+ target
+ ", NULL, &"
+ imgVar
+ ")"
)
image = fb.evaluateExpression("[UIImage imageWithCGImage:" + imgVar + "]")
_showImage(image)
fb.evaluateExpression("CGImageRelease(" + imgVar + ")")
def _dataIsImage(data):
data = "(" + data + ")"
result = fb.evaluateExpressionValue("(id)[UIImage imageWithData:" + data + "]")
if result.GetError() is not None and str(result.GetError()) != "success":
return False
else:
isImage = result.GetValueAsUnsigned() != 0
return isImage
def _dataIsString(data):
data = "(" + data + ")"
result = fb.evaluateExpressionValue(
"(NSString*)[[NSString alloc] initWithData:" + data + " encoding:4]"
)
if result.GetError() is not None and str(result.GetError()) != "success":
return False
else:
isString = result.GetValueAsUnsigned() != 0
return isString
def _visualize(target):
target = fb.evaluateInputExpression(target)
if fb.evaluateBooleanExpression(
"(unsigned long)CFGetTypeID((CFTypeRef)"
+ target
+ ") == (unsigned long)CGImageGetTypeID()"
):
_showImage("(id)[UIImage imageWithCGImage:" + target + "]")
elif fb.evaluateBooleanExpression(
"(unsigned long)CFGetTypeID((CFTypeRef)"
+ target
+ ") == (unsigned long)CVPixelBufferGetTypeID()"
):
_showPixelBuffer(target)
else:
if objectHelpers.isKindOfClass(target, "UIImage"):
_showImage(target)
elif objectHelpers.isKindOfClass(target, "UIView"):
_showLayer("[(id)" + target + " layer]")
elif objectHelpers.isKindOfClass(target, "CALayer"):
_showLayer(target)
elif (
objectHelpers.isKindOfClass(target, "UIColor")
or objectHelpers.isKindOfClass(target, "CIColor")
or _colorIsCGColorRef(target)
):
_showColor(target)
elif objectHelpers.isKindOfClass(target, "NSData"):
if _dataIsImage(target):
_showImage("(id)[UIImage imageWithData:" + target + "]")
elif _dataIsString(target):
print(
fb.describeObject(
"[[NSString alloc] initWithData:" + target + " encoding:4]"
)
)
else:
print("Data isn't an image and isn't a string.")
elif objectHelpers.isKindOfClass(target, "CIImage"):
_showImage("[UIImage imageWithCIImage:(id)" + target + "]")
else:
print(
"{} isn't supported. You can visualize UIImage, CGImageRef, UIView, CALayer, NSData, UIColor, CIColor, CIImage, CGColorRef or CVPixelBuffer.".format(
objectHelpers.className(target)
)
)
class FBVisualizeCommand(fb.FBCommand):
def name(self):
return "visualize"
def description(self):
return "Open a UIImage, CGImageRef, UIView, CALayer, NSData, UIColor, CIColor, CIImage, CGColorRef or CVPixelBuffer in Preview.app on your Mac."
def args(self):
return [
fb.FBCommandArgument(
arg="target", type="(id)", help="The object to visualize."
)
]
def run(self, arguments, options):
_visualize(arguments[0])
================================================
FILE: commands/FBXCTestCommands.py
================================================
#!/usr/bin/python
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
import re
import fbchisellldbbase as fb
import lldb
NOT_FOUND = 0xFFFFFFFF # UINT32_MAX
def lldbcommands():
return [FBXCPrintDebugDescription(), FBXCPrintTree(), FBXCPrintObject(), FBXCNoId()]
class FBXCPrintDebugDescription(fb.FBCommand):
def name(self):
return "xdebug"
def description(self):
return "Print debug description the XCUIElement in human readable format."
def args(self):
return [
fb.FBCommandArgument(
arg="element",
type="XCUIElement*",
help="The element to print debug description.",
default="__default__",
)
]
def run(self, arguments, options):
element = arguments[0]
language = fb.currentLanguage()
if element == "__default__":
element = (
"XCUIApplication()"
if language == lldb.eLanguageTypeSwift
else "(XCUIApplication *)[[XCUIApplication alloc] init]"
)
if language == lldb.eLanguageTypeSwift:
print(
fb.evaluateExpressionValue(
"{}.debugDescription".format(element), language=language
)
.GetObjectDescription()
.replace("\\n", "\n")
.replace("\\'", "'")
.strip(' "\n\t')
)
else:
print(
fb.evaluateExpressionValue(
"[{} debugDescription]".format(element)
).GetObjectDescription()
)
class FBXCPrintTree(fb.FBCommand):
def name(self):
return "xtree"
def description(self):
return "Print XCUIElement subtree."
def args(self):
return [
fb.FBCommandArgument(
arg="element",
type="XCUIElement*",
help="The element to print tree.",
default="__default__",
)
]
def options(self):
return [
fb.FBCommandArgument(
arg="pointer",
short="-
gitextract_gmf84zuc/ ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Chisel/ │ ├── Chisel/ │ │ ├── CHLAllocations.c │ │ ├── CHLAllocations.h │ │ ├── CHLObjcInstanceCommands.h │ │ ├── CHLObjcInstanceCommands.mm │ │ ├── CHLObjcInstances.h │ │ ├── CHLObjcInstances.mm │ │ ├── CHLPredicateTools.h │ │ ├── CHLPredicateTools.m │ │ ├── Chisel.h │ │ ├── Info.plist │ │ └── zone_allocator.h │ ├── Chisel-macOS/ │ │ ├── Chisel_macOS.h │ │ └── Info.plist │ ├── Chisel.xcodeproj/ │ │ ├── project.pbxproj │ │ ├── project.xcworkspace/ │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata/ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata/ │ │ └── xcschemes/ │ │ └── Chisel.xcscheme │ ├── ChiselTests/ │ │ ├── ChiselTests.m │ │ └── Info.plist │ └── Makefile ├── LICENSE ├── README.md ├── commands/ │ ├── FBAccessibilityCommands.py │ ├── FBAutoLayoutCommands.py │ ├── FBClassDump.py │ ├── FBComponentCommands.py │ ├── FBCopyCommands.py │ ├── FBCounterCommands.py │ ├── FBDebugCommands.py │ ├── FBDelay.py │ ├── FBDisplayCommands.py │ ├── FBFindCommands.py │ ├── FBFlickerCommands.py │ ├── FBImportCommands.py │ ├── FBInvocationCommands.py │ ├── FBPrintCommands.py │ ├── FBTextInputCommands.py │ ├── FBVisualizationCommands.py │ └── FBXCTestCommands.py ├── fbchisellldb.py ├── fbchisellldbbase.py ├── fbchisellldbinputhelpers.py ├── fbchisellldbobjcruntimehelpers.py ├── fbchisellldbobjecthelpers.py ├── fbchisellldbviewcontrollerhelpers.py └── fbchisellldbviewhelpers.py
SYMBOL INDEX (630 symbols across 26 files)
FILE: Chisel/Chisel/CHLAllocations.c
function kern_return_t (line 8) | static kern_return_t reader(__unused task_t remote_task, vm_address_t re...
type RangeEnumeratorArgs (line 14) | typedef struct {
function rangeEnumerator (line 19) | static void rangeEnumerator(__unused task_t task, void *context, __unuse...
function CHLScanAllocations (line 27) | void CHLScanAllocations(CHLRangeHandler handler, void *context, const ma...
FILE: Chisel/Chisel/zone_allocator.h
function deallocate (line 22) | void deallocate(T *p, __unused std::size_t n)
function malloc_zone_t (line 27) | const malloc_zone_t *zone() const
FILE: commands/FBAccessibilityCommands.py
function lldbcommands (line 20) | def lldbcommands():
class FBPrintAccessibilityLabels (line 28) | class FBPrintAccessibilityLabels(fb.FBCommand):
method name (line 29) | def name(self):
method description (line 32) | def description(self):
method args (line 35) | def args(self):
method run (line 45) | def run(self, arguments, options):
class FBPrintAccessibilityIdentifiers (line 50) | class FBPrintAccessibilityIdentifiers(fb.FBCommand):
method name (line 51) | def name(self):
method description (line 54) | def description(self):
method args (line 57) | def args(self):
method run (line 67) | def run(self, arguments, option):
class FBFindViewByAccessibilityLabelCommand (line 72) | class FBFindViewByAccessibilityLabelCommand(fb.FBCommand):
method name (line 73) | def name(self):
method description (line 76) | def description(self):
method args (line 82) | def args(self):
method accessibilityGrepHierarchy (line 91) | def accessibilityGrepHierarchy(self, view, needle):
method run (line 131) | def run(self, arguments, options):
function isRunningInSimulator (line 140) | def isRunningInSimulator():
function forceStartAccessibilityServer (line 156) | def forceStartAccessibilityServer():
function accessibilityLabel (line 172) | def accessibilityLabel(view):
function accessibilityIdentifier (line 180) | def accessibilityIdentifier(view):
function accessibilityElements (line 186) | def accessibilityElements(view):
function printAccessibilityHierarchy (line 206) | def printAccessibilityHierarchy(view, indent=0):
function printAccessibilityIdentifiersHierarchy (line 232) | def printAccessibilityIdentifiersHierarchy(view, indent=0):
FILE: commands/FBAutoLayoutCommands.py
function lldbcommands (line 14) | def lldbcommands():
class FBPrintAutolayoutTrace (line 22) | class FBPrintAutolayoutTrace(fb.FBCommand):
method name (line 23) | def name(self):
method description (line 26) | def description(self):
method args (line 32) | def args(self):
method run (line 42) | def run(self, arguments, options):
function setBorderOnAmbiguousViewRecursive (line 51) | def setBorderOnAmbiguousViewRecursive(view, width, color):
class FBAutolayoutBorderAmbiguous (line 74) | class FBAutolayoutBorderAmbiguous(fb.FBCommand):
method name (line 75) | def name(self):
method description (line 78) | def description(self):
method options (line 81) | def options(self):
method run (line 101) | def run(self, arguments, options):
class FBAutolayoutUnborderAmbiguous (line 109) | class FBAutolayoutUnborderAmbiguous(fb.FBCommand):
method name (line 110) | def name(self):
method description (line 113) | def description(self):
method run (line 116) | def run(self, arguments, options):
FILE: commands/FBClassDump.py
function lldbcommands (line 14) | def lldbcommands():
class FBPrintMethods (line 18) | class FBPrintMethods(fb.FBCommand):
method name (line 19) | def name(self):
method description (line 22) | def description(self):
method options (line 25) | def options(self):
method args (line 61) | def args(self):
method run (line 70) | def run(self, arguments, options):
class FBPrintProperties (line 88) | class FBPrintProperties(fb.FBCommand):
method name (line 89) | def name(self):
method description (line 92) | def description(self):
method options (line 95) | def options(self):
method args (line 107) | def args(self):
method run (line 116) | def run(self, arguments, options):
class FBPrintBlock (line 122) | class FBPrintBlock(fb.FBCommand):
method name (line 123) | def name(self):
method description (line 126) | def description(self):
method args (line 129) | def args(self):
method run (line 134) | def run(self, arguments, options):
function isClassObject (line 207) | def isClassObject(arg):
function getClassFromArgument (line 211) | def getClassFromArgument(arg, is_classname):
function printInstanceMethods (line 228) | def printInstanceMethods(cls, showaddr=False, prefix="-"):
function printClassMethods (line 240) | def printClassMethods(cls, showaddr=False):
function printProperties (line 244) | def printProperties(cls, showvalue=False):
function decode (line 250) | def decode(code):
function getMethods (line 290) | def getMethods(klass):
class Method (line 328) | class Method:
method __init__ (line 329) | def __init__(self, json):
method prettyPrintString (line 336) | def prettyPrintString(self):
method toHex (line 348) | def toHex(self, addr):
method __str__ (line 351) | def __str__(self):
function getProperties (line 364) | def getProperties(klass):
class Property (line 397) | class Property:
method __init__ (line 398) | def __init__(self, json):
method prettyPrintString (line 404) | def prettyPrintString(self):
FILE: commands/FBComponentCommands.py
function lldbcommands (line 12) | def lldbcommands():
class FBComponentsDebugCommand (line 20) | class FBComponentsDebugCommand(fb.FBCommand):
method name (line 21) | def name(self):
method description (line 24) | def description(self):
method options (line 27) | def options(self):
method run (line 45) | def run(self, arguments, options):
class FBComponentsPrintCommand (line 49) | class FBComponentsPrintCommand(fb.FBCommand):
method name (line 50) | def name(self):
method description (line 53) | def description(self):
method args (line 58) | def args(self):
method run (line 68) | def run(self, arguments, options):
class FBComponentsReflowCommand (line 82) | class FBComponentsReflowCommand(fb.FBCommand):
method name (line 83) | def name(self):
method description (line 86) | def description(self):
method run (line 89) | def run(self, arguments, options):
FILE: commands/FBCopyCommands.py
function lldbcommands (line 19) | def lldbcommands():
function _copyFromURL (line 23) | def _copyFromURL(url, preferredFilename, noOpen):
function _copyFromData (line 33) | def _copyFromData(data, defaultFilename, preferredFilename, noOpen):
function _copy (line 71) | def _copy(target, preferredFilename, noOpen):
class FBCopyCommand (line 91) | class FBCopyCommand(fb.FBCommand):
method name (line 92) | def name(self):
method description (line 95) | def description(self):
method options (line 98) | def options(self):
method args (line 116) | def args(self):
method run (line 121) | def run(self, arguments, options):
FILE: commands/FBCounterCommands.py
function lldbcommands (line 40) | def lldbcommands():
function generateKey (line 50) | def generateKey(arguments):
class FBIncrementCounterCommand (line 70) | class FBIncrementCounterCommand(fb.FBCommand):
method name (line 71) | def name(self):
method description (line 74) | def description(self):
method run (line 77) | def run(self, arguments, options):
class FBPrintCounterCommand (line 85) | class FBPrintCounterCommand(fb.FBCommand):
method name (line 86) | def name(self):
method description (line 89) | def description(self):
method run (line 92) | def run(self, arguments, options):
class FBPrintCountersCommand (line 100) | class FBPrintCountersCommand(fb.FBCommand):
method name (line 101) | def name(self):
method description (line 104) | def description(self):
method run (line 107) | def run(self, arguments, options):
class FBResetCounterCommand (line 115) | class FBResetCounterCommand(fb.FBCommand):
method name (line 116) | def name(self):
method description (line 119) | def description(self):
method run (line 122) | def run(self, arguments, options):
class FBResetCountersCommand (line 129) | class FBResetCountersCommand(fb.FBCommand):
method name (line 130) | def name(self):
method description (line 133) | def description(self):
method run (line 136) | def run(self, arguments, options):
FILE: commands/FBDebugCommands.py
function lldbcommands (line 20) | def lldbcommands():
class FBWatchInstanceVariableCommand (line 34) | class FBWatchInstanceVariableCommand(fb.FBCommand):
method name (line 35) | def name(self):
method description (line 38) | def description(self):
method args (line 41) | def args(self):
method run (line 51) | def run(self, arguments, options):
class FBFrameworkAddressBreakpointCommand (line 86) | class FBFrameworkAddressBreakpointCommand(fb.FBCommand):
method name (line 87) | def name(self):
method description (line 90) | def description(self):
method args (line 93) | def args(self):
method run (line 102) | def run(self, arguments, options):
class FBMethodBreakpointCommand (line 116) | class FBMethodBreakpointCommand(fb.FBCommand):
method name (line 117) | def name(self):
method description (line 120) | def description(self):
method args (line 123) | def args(self):
method run (line 132) | def run(self, arguments, options):
function classItselfImplementsSelector (line 262) | def classItselfImplementsSelector(klass, selector):
class FBMemoryWarningCommand (line 275) | class FBMemoryWarningCommand(fb.FBCommand):
method name (line 276) | def name(self):
method description (line 279) | def description(self):
method run (line 282) | def run(self, arguments, options):
function switchBreakpointState (line 288) | def switchBreakpointState(expression, on):
class FBMethodBreakpointEnableCommand (line 307) | class FBMethodBreakpointEnableCommand(fb.FBCommand):
method name (line 308) | def name(self):
method description (line 311) | def description(self):
method args (line 330) | def args(self):
method run (line 337) | def run(self, arguments, options):
class FBMethodBreakpointDisableCommand (line 342) | class FBMethodBreakpointDisableCommand(fb.FBCommand):
method name (line 343) | def name(self):
method description (line 346) | def description(self):
method args (line 365) | def args(self):
method run (line 372) | def run(self, arguments, options):
class FBFindInstancesCommand (line 377) | class FBFindInstancesCommand(fb.FBCommand):
method name (line 378) | def name(self):
method args (line 381) | def args(self):
method description (line 391) | def description(self):
method lex (line 425) | def lex(self, commandLine):
method run (line 430) | def run(self, arguments, options):
method loadChiselIfNecessary (line 447) | def loadChiselIfNecessary(self):
method chiselLibraryPath (line 482) | def chiselLibraryPath(self):
class FBHeapFromCommand (line 494) | class FBHeapFromCommand(fb.FBCommand):
method name (line 495) | def name(self):
method description (line 498) | def description(self):
method run (line 501) | def run(self, arguments, options):
class FBSequenceCommand (line 547) | class FBSequenceCommand(fb.FBCommand):
method name (line 548) | def name(self):
method description (line 551) | def description(self):
method lex (line 554) | def lex(self, commandLine):
method run (line 557) | def run(self, arguments, options):
method run_command (line 588) | def run_command(self, interpreter, command):
method is_continue (line 601) | def is_continue(self, interpreter, command):
FILE: commands/FBDelay.py
function lldbcommands (line 14) | def lldbcommands():
class FBDelay (line 18) | class FBDelay(fb.FBCommand):
method name (line 19) | def name(self):
method description (line 22) | def description(self):
method args (line 25) | def args(self):
method run (line 40) | def run(self, arguments, options):
method runDelayed (line 48) | def runDelayed(self, command):
FILE: commands/FBDisplayCommands.py
function lldbcommands (line 15) | def lldbcommands():
class FBDrawBorderCommand (line 31) | class FBDrawBorderCommand(fb.FBCommand):
method name (line 46) | def name(self):
method description (line 49) | def description(self):
method args (line 52) | def args(self):
method options (line 61) | def options(self):
method run (line 89) | def run(self, args, options):
method nextColorAfterColor (line 128) | def nextColorAfterColor(self, color):
class FBRemoveBorderCommand (line 133) | class FBRemoveBorderCommand(fb.FBCommand):
method name (line 134) | def name(self):
method description (line 137) | def description(self):
method options (line 140) | def options(self):
method args (line 152) | def args(self):
method run (line 161) | def run(self, args, options):
class FBMaskViewCommand (line 184) | class FBMaskViewCommand(fb.FBCommand):
method name (line 185) | def name(self):
method description (line 188) | def description(self):
method args (line 191) | def args(self):
method options (line 200) | def options(self):
method run (line 220) | def run(self, args, options):
class FBUnmaskViewCommand (line 225) | class FBUnmaskViewCommand(fb.FBCommand):
method name (line 226) | def name(self):
method description (line 229) | def description(self):
method args (line 232) | def args(self):
method run (line 241) | def run(self, args, options):
class FBCoreAnimationFlushCommand (line 246) | class FBCoreAnimationFlushCommand(fb.FBCommand):
method name (line 247) | def name(self):
method description (line 250) | def description(self):
method run (line 253) | def run(self, arguments, options):
class FBShowViewCommand (line 257) | class FBShowViewCommand(fb.FBCommand):
method name (line 258) | def name(self):
method description (line 261) | def description(self):
method args (line 264) | def args(self):
method run (line 273) | def run(self, args, options):
class FBHideViewCommand (line 277) | class FBHideViewCommand(fb.FBCommand):
method name (line 278) | def name(self):
method description (line 281) | def description(self):
method args (line 284) | def args(self):
method run (line 293) | def run(self, args, options):
class FBPresentViewControllerCommand (line 297) | class FBPresentViewControllerCommand(fb.FBCommand):
method name (line 298) | def name(self):
method description (line 301) | def description(self):
method args (line 304) | def args(self):
method run (line 313) | def run(self, args, option):
class FBDismissViewControllerCommand (line 317) | class FBDismissViewControllerCommand(fb.FBCommand):
method name (line 318) | def name(self):
method description (line 321) | def description(self):
method args (line 324) | def args(self):
method run (line 333) | def run(self, args, option):
class FBSlowAnimationCommand (line 337) | class FBSlowAnimationCommand(fb.FBCommand):
method name (line 338) | def name(self):
method description (line 341) | def description(self):
method args (line 344) | def args(self):
method run (line 354) | def run(self, args, option):
class FBUnslowAnimationCommand (line 358) | class FBUnslowAnimationCommand(fb.FBCommand):
method name (line 359) | def name(self):
method description (line 362) | def description(self):
method run (line 365) | def run(self, args, option):
FILE: commands/FBFindCommands.py
function lldbcommands (line 17) | def lldbcommands():
class FBFindViewControllerCommand (line 21) | class FBFindViewControllerCommand(fb.FBCommand):
method name (line 22) | def name(self):
method description (line 25) | def description(self):
method options (line 28) | def options(self):
method run (line 46) | def run(self, arguments, options):
method findOwningViewController (line 60) | def findOwningViewController(self, object):
method isViewController (line 73) | def isViewController(object):
method nextResponder (line 79) | def nextResponder(object):
class FBFindViewCommand (line 91) | class FBFindViewCommand(fb.FBCommand):
method name (line 92) | def name(self):
method description (line 95) | def description(self):
method args (line 98) | def args(self):
method run (line 107) | def run(self, arguments, options):
function printMatchesInViewOutputStringAndCopyFirstToClipboard (line 114) | def printMatchesInViewOutputStringAndCopyFirstToClipboard(needle, haysta...
class FBTapLoggerCommand (line 130) | class FBTapLoggerCommand(fb.FBCommand):
method name (line 131) | def name(self):
method description (line 134) | def description(self):
method run (line 137) | def run(self, arguments, options):
function taplog_callback (line 162) | def taplog_callback(frame, bp_loc, internal_dict):
FILE: commands/FBFlickerCommands.py
function lldbcommands (line 17) | def lldbcommands():
class FBFlickerViewCommand (line 21) | class FBFlickerViewCommand(fb.FBCommand):
method name (line 22) | def name(self):
method description (line 25) | def description(self):
method args (line 28) | def args(self):
method run (line 35) | def run(self, arguments, options):
class FBViewSearchCommand (line 45) | class FBViewSearchCommand(fb.FBCommand):
method name (line 46) | def name(self):
method description (line 49) | def description(self):
method args (line 52) | def args(self):
method run (line 57) | def run(self, arguments, options):
class FlickerWalker (line 67) | class FlickerWalker:
method __init__ (line 68) | def __init__(self, startView):
method run (line 71) | def run(self):
method inputCallback (line 83) | def inputCallback(self, input):
method setCurrentView (line 129) | def setCurrentView(self, view, oldView=None):
function superviewOfView (line 138) | def superviewOfView(view):
function subviewsOfView (line 146) | def subviewsOfView(view):
function firstSubviewOfView (line 150) | def firstSubviewOfView(view):
function nthSiblingOfView (line 160) | def nthSiblingOfView(view, n):
FILE: commands/FBImportCommands.py
function lldbcommands (line 12) | def lldbcommands():
class ImportUIKitModule (line 16) | class ImportUIKitModule(fb.FBCommand):
method name (line 17) | def name(self):
method description (line 20) | def description(self):
method run (line 23) | def run(self, arguments, options):
FILE: commands/FBInvocationCommands.py
function lldbcommands (line 14) | def lldbcommands():
class FBPrintInvocation (line 18) | class FBPrintInvocation(fb.FBCommand):
method name (line 19) | def name(self):
method description (line 22) | def description(self):
method options (line 25) | def options(self):
method run (line 37) | def run(self, arguments, options):
function printInvocationForFrame (line 55) | def printInvocationForFrame(frame):
function stackStartAddressInSelectedFrame (line 101) | def stackStartAddressInSelectedFrame(frame):
function findArgAtIndexFromStackFrame (line 121) | def findArgAtIndexFromStackFrame(frame, index):
function findArgAdressAtIndexFromStackFrame (line 127) | def findArgAdressAtIndexFromStackFrame(frame, index):
function prettyPrintInvocation (line 133) | def prettyPrintInvocation(frame, invocation):
function argumentAsString (line 173) | def argumentAsString(frame, address, encoding): # noqa C901
FILE: commands/FBPrintCommands.py
function lldbcommands (line 19) | def lldbcommands():
class FBPrintViewHierarchyCommand (line 43) | class FBPrintViewHierarchyCommand(fb.FBCommand):
method name (line 44) | def name(self):
method description (line 47) | def description(self):
method options (line 50) | def options(self):
method args (line 94) | def args(self):
method run (line 104) | def run(self, arguments, options):
class FBPrintViewControllerHierarchyCommand (line 163) | class FBPrintViewControllerHierarchyCommand(fb.FBCommand):
method name (line 164) | def name(self):
method description (line 167) | def description(self):
method args (line 170) | def args(self):
method run (line 180) | def run(self, arguments, options):
class FBPrintIsExecutingInAnimationBlockCommand (line 201) | class FBPrintIsExecutingInAnimationBlockCommand(fb.FBCommand):
method name (line 202) | def name(self):
method description (line 205) | def description(self):
method run (line 210) | def run(self, arguments, options):
function _printIterative (line 214) | def _printIterative(initialValue, generator):
class FBPrintInheritanceHierarchy (line 221) | class FBPrintInheritanceHierarchy(fb.FBCommand):
method name (line 222) | def name(self):
method description (line 225) | def description(self):
method args (line 228) | def args(self):
method run (line 235) | def run(self, arguments, options):
function _inheritanceHierarchy (line 239) | def _inheritanceHierarchy(instanceOfAClass):
class FBPrintUpwardResponderChain (line 249) | class FBPrintUpwardResponderChain(fb.FBCommand):
method name (line 250) | def name(self):
method description (line 253) | def description(self):
method args (line 256) | def args(self):
method run (line 265) | def run(self, arguments, options):
function _responderChain (line 286) | def _responderChain(startResponder):
function tableViewInHierarchy (line 295) | def tableViewInHierarchy():
class FBPrintOnscreenTableView (line 328) | class FBPrintOnscreenTableView(fb.FBCommand):
method name (line 329) | def name(self):
method description (line 332) | def description(self):
method run (line 335) | def run(self, arguments, options):
class FBPrintOnscreenTableViewCells (line 346) | class FBPrintOnscreenTableViewCells(fb.FBCommand):
method name (line 347) | def name(self):
method description (line 350) | def description(self):
method run (line 353) | def run(self, arguments, options):
class FBPrintInternals (line 362) | class FBPrintInternals(fb.FBCommand):
method name (line 363) | def name(self):
method description (line 366) | def description(self):
method args (line 369) | def args(self):
method options (line 376) | def options(self):
method run (line 388) | def run(self, arguments, options):
class FBPrintInstanceVariable (line 406) | class FBPrintInstanceVariable(fb.FBCommand):
method name (line 407) | def name(self):
method description (line 410) | def description(self):
method args (line 413) | def args(self):
method run (line 423) | def run(self, arguments, options):
class FBPrintKeyPath (line 446) | class FBPrintKeyPath(fb.FBCommand):
method name (line 447) | def name(self):
method description (line 450) | def description(self):
method args (line 453) | def args(self):
method run (line 460) | def run(self, arguments, options):
class FBPrintApplicationDocumentsPath (line 472) | class FBPrintApplicationDocumentsPath(fb.FBCommand):
method name (line 473) | def name(self):
method description (line 476) | def description(self):
method options (line 479) | def options(self):
method run (line 491) | def run(self, arguments, options):
class FBPrintApplicationBundlePath (line 510) | class FBPrintApplicationBundlePath(fb.FBCommand):
method name (line 511) | def name(self):
method description (line 514) | def description(self):
method options (line 517) | def options(self):
method run (line 529) | def run(self, arguments, options):
class FBPrintData (line 541) | class FBPrintData(fb.FBCommand):
method name (line 542) | def name(self):
method description (line 545) | def description(self):
method options (line 566) | def options(self):
method args (line 578) | def args(self):
method run (line 583) | def run(self, arguments, option): # noqa C901
class FBPrintTargetActions (line 635) | class FBPrintTargetActions(fb.FBCommand):
method name (line 636) | def name(self):
method description (line 639) | def description(self):
method args (line 642) | def args(self):
method run (line 651) | def run(self, arguments, options):
class FBPrintJSON (line 684) | class FBPrintJSON(fb.FBCommand):
method name (line 685) | def name(self):
method description (line 688) | def description(self):
method options (line 691) | def options(self):
method args (line 703) | def args(self):
method run (line 712) | def run(self, arguments, options):
class FBPrintSwiftJSON (line 729) | class FBPrintSwiftJSON(fb.FBCommand):
method name (line 730) | def name(self):
method description (line 733) | def description(self):
method options (line 736) | def options(self):
method args (line 748) | def args(self):
method run (line 757) | def run(self, arguments, options):
class FBPrintAsCurl (line 777) | class FBPrintAsCurl(fb.FBCommand):
method name (line 778) | def name(self):
method description (line 781) | def description(self):
method options (line 784) | def options(self):
method args (line 796) | def args(self):
method generateTmpFilePath (line 805) | def generateTmpFilePath(self):
method run (line 812) | def run(self, arguments, options):
class FBPrintToClipboard (line 891) | class FBPrintToClipboard(fb.FBCommand):
method name (line 892) | def name(self):
method description (line 895) | def description(self):
method args (line 898) | def args(self):
method run (line 903) | def run(self, arguments, options):
class FBPrintObjectInObjc (line 914) | class FBPrintObjectInObjc(fb.FBCommand):
method name (line 915) | def name(self):
method description (line 918) | def description(self):
method args (line 921) | def args(self):
method run (line 928) | def run(self, arguments, options):
FILE: commands/FBTextInputCommands.py
function lldbcommands (line 17) | def lldbcommands():
class FBInputTexByAccessibilityIdCommand (line 21) | class FBInputTexByAccessibilityIdCommand(fb.FBCommand):
method name (line 22) | def name(self):
method description (line 25) | def description(self):
method args (line 28) | def args(self):
method run (line 40) | def run(self, arguments, options):
method findView (line 45) | def findView(self, view, searchIdentifier, replacementText):
class FBInputTexToFirstResponderCommand (line 56) | class FBInputTexToFirstResponderCommand(fb.FBCommand):
method name (line 57) | def name(self):
method description (line 60) | def description(self):
method args (line 63) | def args(self):
method run (line 70) | def run(self, arguments, options):
method findFirstResponder (line 73) | def findFirstResponder(self, view, replacementText):
function rootView (line 84) | def rootView():
function subviewsOfView (line 88) | def subviewsOfView(view):
function subviewAtIndex (line 92) | def subviewAtIndex(views, index):
function viewsCount (line 96) | def viewsCount(views):
function accessibilityIdentifier (line 100) | def accessibilityIdentifier(view):
function isEqualToString (line 104) | def isEqualToString(identifier, needle):
function setTextInView (line 110) | def setTextInView(view, text):
function isFirstResponder (line 115) | def isFirstResponder(view):
FILE: commands/FBVisualizationCommands.py
function lldbcommands (line 17) | def lldbcommands():
function _showImage (line 21) | def _showImage(commandForImage):
function _colorIsCGColorRef (line 63) | def _colorIsCGColorRef(color):
function _showColor (line 80) | def _showColor(color):
function _showLayer (line 118) | def _showLayer(layer):
function _showPixelBuffer (line 149) | def _showPixelBuffer(target):
function _dataIsImage (line 164) | def _dataIsImage(data):
function _dataIsString (line 176) | def _dataIsString(data):
function _visualize (line 190) | def _visualize(target):
class FBVisualizeCommand (line 239) | class FBVisualizeCommand(fb.FBCommand):
method name (line 240) | def name(self):
method description (line 243) | def description(self):
method args (line 246) | def args(self):
method run (line 253) | def run(self, arguments, options):
FILE: commands/FBXCTestCommands.py
function lldbcommands (line 17) | def lldbcommands():
class FBXCPrintDebugDescription (line 21) | class FBXCPrintDebugDescription(fb.FBCommand):
method name (line 22) | def name(self):
method description (line 25) | def description(self):
method args (line 28) | def args(self):
method run (line 38) | def run(self, arguments, options):
class FBXCPrintTree (line 67) | class FBXCPrintTree(fb.FBCommand):
method name (line 68) | def name(self):
method description (line 71) | def description(self):
method args (line 74) | def args(self):
method options (line 84) | def options(self):
method run (line 115) | def run(self, arguments, options):
class FBXCPrintObject (line 146) | class FBXCPrintObject(fb.FBCommand):
method name (line 147) | def name(self):
method description (line 150) | def description(self):
method args (line 153) | def args(self):
method run (line 163) | def run(self, arguments, options):
class FBXCNoId (line 190) | class FBXCNoId(fb.FBCommand):
method name (line 191) | def name(self):
method description (line 194) | def description(self):
method args (line 197) | def args(self):
method options (line 207) | def options(self):
method run (line 247) | def run(self, arguments, options):
function take_snapshot (line 284) | def take_snapshot(element):
class _ElementList (line 299) | class _ElementList(object):
method __init__ (line 307) | def __init__(self, element, children):
method text (line 311) | def text(self, pointer, trait, frame, indent):
method hierarchy_text (line 328) | def hierarchy_text(self, pointer=False, trait=False, frame=False, inde...
class XCElementSnapshot (line 347) | class XCElementSnapshot(object):
method __init__ (line 373) | def __init__(self, element, language):
method is_missing_identifier (line 403) | def is_missing_identifier(self):
method type (line 413) | def type(self):
method type_value (line 429) | def type_value(self):
method type_summary (line 437) | def type_summary(self):
method traits (line 445) | def traits(self):
method traits_value (line 461) | def traits_value(self):
method traits_summary (line 469) | def traits_summary(self):
method frame (line 477) | def frame(self):
method frame_summary (line 494) | def frame_summary(self):
method identifier (line 502) | def identifier(self):
method identifier_value (line 518) | def identifier_value(self):
method identifier_summary (line 526) | def identifier_summary(self):
method value (line 536) | def value(self):
method value_value (line 552) | def value_value(self):
method value_summary (line 560) | def value_summary(self):
method placeholder (line 570) | def placeholder(self):
method placeholder_value (line 586) | def placeholder_value(self):
method placeholder_summary (line 594) | def placeholder_summary(self):
method label (line 604) | def label(self):
method label_value (line 620) | def label_value(self):
method label_summary (line 628) | def label_summary(self):
method title (line 638) | def title(self):
method title_value (line 654) | def title_value(self):
method title_summary (line 662) | def title_summary(self):
method children (line 672) | def children(self):
method children_count (line 688) | def children_count(self):
method children_list (line 696) | def children_list(self):
method enabled (line 704) | def enabled(self):
method enabled_value (line 720) | def enabled_value(self):
method enabled_summary (line 728) | def enabled_summary(self):
method selected (line 738) | def selected(self):
method selected_value (line 754) | def selected_value(self):
method selected_summary (line 762) | def selected_summary(self):
method is_main_window (line 772) | def is_main_window(self):
method is_main_window_value (line 788) | def is_main_window_value(self):
method is_main_window_summary (line 796) | def is_main_window_summary(self):
method keyboard_focus (line 806) | def keyboard_focus(self):
method keyboard_focus_value (line 822) | def keyboard_focus_value(self):
method keyboard_focus_summary (line 830) | def keyboard_focus_summary(self):
method focus (line 840) | def focus(self):
method focus_value (line 856) | def focus_value(self):
method focus_summary (line 864) | def focus_summary(self):
method generation (line 874) | def generation(self):
method generation_value (line 890) | def generation_value(self):
method horizontal_size_class (line 898) | def horizontal_size_class(self):
method horizontal_size_class_value (line 914) | def horizontal_size_class_value(self):
method horizontal_size_class_summary (line 922) | def horizontal_size_class_summary(self):
method vertical_size_class (line 931) | def vertical_size_class(self):
method vertical_size_class_value (line 947) | def vertical_size_class_value(self):
method vertical_size_class_summary (line 955) | def vertical_size_class_summary(self):
method uniquely_identifying_objective_c_code (line 962) | def uniquely_identifying_objective_c_code(self):
method uniquely_identifying_objective_c_code_value (line 972) | def uniquely_identifying_objective_c_code_value(self):
method uniquely_identifying_swift_code (line 982) | def uniquely_identifying_swift_code(self):
method uniquely_identifying_swift_code_value (line 992) | def uniquely_identifying_swift_code_value(self):
method is_touch_bar_element (line 1002) | def is_touch_bar_element(self):
method is_touch_bar_element_value (line 1012) | def is_touch_bar_element_value(self):
method is_top_level_touch_bar_element (line 1020) | def is_top_level_touch_bar_element(self):
method is_top_level_touch_bar_element_value (line 1030) | def is_top_level_touch_bar_element_value(self):
method suggested_hit_points (line 1038) | def suggested_hit_points(self):
method suggested_hit_points_value (line 1048) | def suggested_hit_points_value(self):
method visible_frame (line 1058) | def visible_frame(self):
method visible_frame_summary (line 1069) | def visible_frame_summary(self):
method depth (line 1077) | def depth(self):
method depth_value (line 1085) | def depth_value(self):
method hit_point (line 1093) | def hit_point(self):
method hit_point_value (line 1104) | def hit_point_value(self):
method hit_point_for_scrolling (line 1112) | def hit_point_for_scrolling(self):
method hit_point_for_scrolling_value (line 1123) | def hit_point_for_scrolling_value(self):
method summary (line 1130) | def summary(self, pointer=False, trait=False, frame=False):
method detail_summary (line 1180) | def detail_summary(self):
method tree (line 1234) | def tree(self):
method find_missing_identifiers (line 1246) | def find_missing_identifiers(self, status_bar):
method get_type_value_string (line 1273) | def get_type_value_string(value):
method get_traits_value_string (line 1284) | def get_traits_value_string(value):
method get_user_interface_size_class_string (line 1295) | def get_user_interface_size_class_string(value):
class XCUIElementType (line 1306) | class XCUIElementType(object):
method _attributes_by_value (line 1395) | def _attributes_by_value(cls):
method name_for_value (line 1410) | def name_for_value(cls, value):
class UIAccessibilityTraits (line 1425) | class UIAccessibilityTraits(object):
method _attributes_by_value (line 1449) | def _attributes_by_value(cls):
method name_for_value (line 1464) | def name_for_value(cls, value):
class UIUserInterfaceSizeClass (line 1487) | class UIUserInterfaceSizeClass(object):
method name_for_value (line 1497) | def name_for_value(cls, value):
class CGRect (line 1515) | class CGRect(object):
method __init__ (line 1522) | def __init__(self, element):
method summary (line 1530) | def summary(self):
class CGPoint (line 1547) | class CGPoint(object):
method __init__ (line 1554) | def __init__(self, element):
method summary (line 1559) | def summary(self):
function normalize_summary (line 1572) | def normalize_summary(summary):
function normalize_array_description (line 1583) | def normalize_array_description(description):
function import_uikit (line 1597) | def import_uikit():
function debug (line 1608) | def debug(element):
FILE: fbchisellldb.py
function __lldb_init_module (line 16) | def __lldb_init_module(debugger, dict):
function loadCommandsInDirectory (line 24) | def loadCommandsInDirectory(commandsDirectory):
function loadCommand (line 41) | def loadCommand(module, command, directory, filename, extension):
function makeRunCommand (line 72) | def makeRunCommand(command, filename):
function validateArgsForCommand (line 112) | def validateArgsForCommand(args, command):
function optionParserForCommand (line 130) | def optionParserForCommand(command):
function helpForCommand (line 154) | def helpForCommand(command, filename):
function usageForCommand (line 201) | def usageForCommand(command):
FILE: fbchisellldbbase.py
class FBCommandArgument (line 14) | class FBCommandArgument: # noqa B903
method __init__ (line 15) | def __init__(
class FBCommand (line 27) | class FBCommand:
method name (line 28) | def name(self):
method options (line 31) | def options(self):
method args (line 34) | def args(self):
method description (line 37) | def description(self):
method lex (line 40) | def lex(self, commandLine):
method run (line 43) | def run(self, arguments, option):
function isSuccess (line 47) | def isSuccess(error):
function importModule (line 56) | def importModule(frame, module):
function evaluateExpressionValue (line 65) | def evaluateExpressionValue(
function evaluateInputExpression (line 110) | def evaluateInputExpression(expression, printErrors=True):
function evaluateIntegerExpression (line 132) | def evaluateIntegerExpression(expression, printErrors=True):
function evaluateBooleanExpression (line 143) | def evaluateBooleanExpression(expression, printErrors=True):
function evaluateExpression (line 149) | def evaluateExpression(expression, printErrors=True):
function describeObject (line 153) | def describeObject(expression, printErrors=True):
function evaluateEffect (line 159) | def evaluateEffect(expression, printErrors=True):
function evaluateObjectExpression (line 163) | def evaluateObjectExpression(expression, printErrors=True):
function evaluateCStringExpression (line 167) | def evaluateCStringExpression(expression, printErrors=True):
function check_expr (line 200) | def check_expr(expr):
function evaluate (line 210) | def evaluate(expr):
function currentLanguage (line 233) | def currentLanguage():
FILE: fbchisellldbinputhelpers.py
class FBInputHandler (line 11) | class FBInputHandler:
method __init__ (line 12) | def __init__(self, debugger, callback):
method isValid (line 25) | def isValid(self):
method start (line 28) | def start(self):
method stop (line 31) | def stop(self):
method handleInput (line 34) | def handleInput(self, inputReader, notification, bytes):
FILE: fbchisellldbobjcruntimehelpers.py
function objc_getClass (line 14) | def objc_getClass(className):
function object_getClass (line 20) | def object_getClass(object):
function class_getName (line 26) | def class_getName(klass):
function class_getSuperclass (line 32) | def class_getSuperclass(klass):
function class_isMetaClass (line 38) | def class_isMetaClass(klass):
function class_getInstanceMethod (line 43) | def class_getInstanceMethod(klass, selector):
function currentArch (line 51) | def currentArch():
function functionPreambleExpressionForSelf (line 59) | def functionPreambleExpressionForSelf():
function functionPreambleExpressionForObjectParameterAtIndex (line 75) | def functionPreambleExpressionForObjectParameterAtIndex(parameterIndex):
function isMacintoshArch (line 102) | def isMacintoshArch():
function isIOSSimulator (line 113) | def isIOSSimulator():
function isIOSDevice (line 123) | def isIOSDevice():
FILE: fbchisellldbobjecthelpers.py
function isKindOfClass (line 11) | def isKindOfClass(obj, className):
function className (line 16) | def className(obj):
FILE: fbchisellldbviewcontrollerhelpers.py
function presentViewController (line 12) | def presentViewController(viewController):
function dismissViewController (line 34) | def dismissViewController(viewController):
function viewControllerRecursiveDescription (line 56) | def viewControllerRecursiveDescription(vc):
function _viewControllerDescription (line 62) | def _viewControllerDescription(viewController):
function _recursiveViewControllerDescriptionWithPrefixAndChildPrefix (line 82) | def _recursiveViewControllerDescriptionWithPrefixAndChildPrefix(
FILE: fbchisellldbviewhelpers.py
function flushCoreAnimationTransaction (line 12) | def flushCoreAnimationTransaction():
function setViewHidden (line 16) | def setViewHidden(object, hidden):
function maskView (line 21) | def maskView(viewOrLayer, color, alpha):
function unmaskView (line 46) | def unmaskView(viewOrLayer):
function convertPoint (line 57) | def convertPoint(x, y, fromViewOrLayer, toViewOrLayer):
function convertToLayer (line 66) | def convertToLayer(viewOrLayer):
function isUIView (line 79) | def isUIView(obj):
function isNSView (line 85) | def isNSView(obj):
function isView (line 91) | def isView(obj):
function subviewsOfView (line 97) | def subviewsOfView(view):
function upwardsRecursiveDescription (line 110) | def upwardsRecursiveDescription(view, maxDepth=0):
function slowAnimation (line 150) | def slowAnimation(speed=1):
Condensed preview — 50 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (286K chars).
[
{
"path": ".gitignore",
"chars": 6,
"preview": "*.pyc\n"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 241,
"preview": "# Code of Conduct\nFacebook has adopted a Code of Conduct that we expect project participants to adhere to. Please [read "
},
{
"path": "CONTRIBUTING.md",
"chars": 2023,
"preview": "# Contributing to Chisel\nWe want to make contributing to this project as easy and transparent as\npossible.\n\n## Pull Requ"
},
{
"path": "Chisel/Chisel/CHLAllocations.c",
"chars": 1334,
"preview": "// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n//\n// This source code is licensed under the MIT"
},
{
"path": "Chisel/Chisel/CHLAllocations.h",
"chars": 628,
"preview": "// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n//\n// This source code is licensed under the MIT"
},
{
"path": "Chisel/Chisel/CHLObjcInstanceCommands.h",
"chars": 519,
"preview": "// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n//\n// This source code is licensed under the MIT"
},
{
"path": "Chisel/Chisel/CHLObjcInstanceCommands.mm",
"chars": 5947,
"preview": "// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n//\n// This source code is licensed under the MIT"
},
{
"path": "Chisel/Chisel/CHLObjcInstances.h",
"chars": 829,
"preview": "// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n//\n// This source code is licensed under the MIT"
},
{
"path": "Chisel/Chisel/CHLObjcInstances.mm",
"chars": 5508,
"preview": "// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n//\n// This source code is licensed under the MIT"
},
{
"path": "Chisel/Chisel/CHLPredicateTools.h",
"chars": 370,
"preview": "// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n//\n// This source code is licensed under the MIT"
},
{
"path": "Chisel/Chisel/CHLPredicateTools.m",
"chars": 2167,
"preview": "// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n//\n// This source code is licensed under the MIT"
},
{
"path": "Chisel/Chisel/Chisel.h",
"chars": 546,
"preview": "// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n//\n// This source code is licensed under the MIT"
},
{
"path": "Chisel/Chisel/Info.plist",
"chars": 753,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Chisel/Chisel/zone_allocator.h",
"chars": 1047,
"preview": "// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n//\n// This source code is licensed under the MIT"
},
{
"path": "Chisel/Chisel-macOS/Chisel_macOS.h",
"chars": 578,
"preview": "// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n//\n// This source code is licensed under the MIT"
},
{
"path": "Chisel/Chisel-macOS/Info.plist",
"chars": 774,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Chisel/Chisel.xcodeproj/project.pbxproj",
"chars": 25014,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "Chisel/Chisel.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
"chars": 151,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"self:Chisel.xcodepro"
},
{
"path": "Chisel/Chisel.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
"chars": 238,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Chisel/Chisel.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
"chars": 243,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Chisel/Chisel.xcodeproj/xcshareddata/xcschemes/Chisel.xcscheme",
"chars": 3668,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"0920\"\n version = \"1.3\">\n <BuildAction\n "
},
{
"path": "Chisel/ChiselTests/ChiselTests.m",
"chars": 959,
"preview": "// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n//\n// This source code is licensed under the MIT"
},
{
"path": "Chisel/ChiselTests/Info.plist",
"chars": 680,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Chisel/Makefile",
"chars": 321,
"preview": "PREFIX ?= /usr/local/lib\n\nexport INSTALL_NAME =\nifneq ($(LD_DYLIB_INSTALL_NAME),)\n\tINSTALL_NAME = \"LD_DYLIB_INSTALL_NAME"
},
{
"path": "LICENSE",
"chars": 1085,
"preview": "MIT License\n\nCopyright (c) Facebook, Inc. and its affiliates.\n\nPermission is hereby granted, free of charge, to any pers"
},
{
"path": "README.md",
"chars": 6009,
"preview": "# Chisel\n`Chisel` is a collection of `LLDB` commands to assist in the debugging of iOS apps.\n\n[[Installation](#installat"
},
{
"path": "commands/FBAccessibilityCommands.py",
"chars": 9109,
"preview": "#!/usr/bin/python\n\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n#\n# This source code is licens"
},
{
"path": "commands/FBAutoLayoutCommands.py",
"chars": 3875,
"preview": "#!/usr/bin/python\n\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n#\n# This source code is licens"
},
{
"path": "commands/FBClassDump.py",
"chars": 14163,
"preview": "#!/usr/bin/python\n\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n#\n# This source code is licens"
},
{
"path": "commands/FBComponentCommands.py",
"chars": 2602,
"preview": "#!/usr/bin/python\n\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n#\n# This source code is licens"
},
{
"path": "commands/FBCopyCommands.py",
"chars": 3291,
"preview": "#!/usr/bin/python\n\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n#\n# This source code is licens"
},
{
"path": "commands/FBCounterCommands.py",
"chars": 3920,
"preview": "#!/usr/bin/python\n\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n#\n# This source code is licens"
},
{
"path": "commands/FBDebugCommands.py",
"chars": 20677,
"preview": "#!/usr/bin/python\n\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n#\n# This source code is licens"
},
{
"path": "commands/FBDelay.py",
"chars": 1393,
"preview": "#!/usr/bin/python\n\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n#\n# This source code is licens"
},
{
"path": "commands/FBDisplayCommands.py",
"chars": 10549,
"preview": "#!/usr/bin/python\n\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n#\n# This source code is licens"
},
{
"path": "commands/FBFindCommands.py",
"chars": 6018,
"preview": "#!/usr/bin/python\n\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n#\n# This source code is licens"
},
{
"path": "commands/FBFlickerCommands.py",
"chars": 5454,
"preview": "#!/usr/bin/python\n\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n#\n# This source code is licens"
},
{
"path": "commands/FBImportCommands.py",
"chars": 755,
"preview": "#!/usr/bin/python\n\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n#\n# This source code is licens"
},
{
"path": "commands/FBInvocationCommands.py",
"chars": 6764,
"preview": "#!/usr/bin/python\n\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n#\n# This source code is licens"
},
{
"path": "commands/FBPrintCommands.py",
"chars": 30071,
"preview": "#!/usr/bin/python\n\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n#\n# This source code is licens"
},
{
"path": "commands/FBTextInputCommands.py",
"chars": 3413,
"preview": "#!/usr/bin/python\n\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n#\n# This source code is licens"
},
{
"path": "commands/FBVisualizationCommands.py",
"chars": 8003,
"preview": "#!/usr/bin/python\n\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n#\n# This source code is licens"
},
{
"path": "commands/FBXCTestCommands.py",
"chars": 48565,
"preview": "#!/usr/bin/python\n\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n#\n# This source code is licens"
},
{
"path": "fbchisellldb.py",
"chars": 6814,
"preview": "#!/usr/bin/python\n\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n#\n# This source code is licens"
},
{
"path": "fbchisellldbbase.py",
"chars": 7180,
"preview": "#!/usr/bin/python\n\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n#\n# This source code is licens"
},
{
"path": "fbchisellldbinputhelpers.py",
"chars": 1094,
"preview": "#!/usr/bin/python\n\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n#\n# This source code is licens"
},
{
"path": "fbchisellldbobjcruntimehelpers.py",
"chars": 3509,
"preview": "#!/usr/bin/python\n\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n#\n# This source code is licens"
},
{
"path": "fbchisellldbobjecthelpers.py",
"chars": 554,
"preview": "#!/usr/bin/python\n\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n#\n# This source code is licens"
},
{
"path": "fbchisellldbviewcontrollerhelpers.py",
"chars": 4320,
"preview": "#!/usr/bin/python\n\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n#\n# This source code is licens"
},
{
"path": "fbchisellldbviewhelpers.py",
"chars": 5059,
"preview": "#!/usr/bin/python\n\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n#\n# This source code is licens"
}
]
About this extraction
This page contains the full source code of the facebook/chisel GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 50 files (262.5 KB), approximately 64.5k tokens, and a symbol index with 630 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.