常用lldb

Chapter 1 Gettting Started 概览

常用寄存器(x86_64)
• FirstArgument:RDI
• SecondArgument:RSI
• ThirdArgument:RDX
• FourthArgument:RCX
• FifthArgument:R8
• SixthArgument:R9

  • 返回值RAX

常用寄存器 (arm64)

  • x1

真机

[Tab 1]
$ iproxy 5000 22
[Tab 2]
$ iproxy 1234 1234
[Tab 3]
$ ssh root@localhost -p 5000
root# ps -e | grep Preferences
root# debugserver *:1234 -attach=40776
[Tab 4]
lldb
(lldb) process connect connect://localhost:1234
[Terminal 1]
$ tty
/dev/ttys003

[Terminal 2]
(lldb) file /Applications/Xcode.app/Contents/MacOS/Xcode
(lldb) process launch -e /dev/ttys003
(Ctrl + C)
(lldb) breakpoint set -n "-[NSView hitTest:]"
Breakpoint 1: where = AppKit`-[NSView hitTest:], address =
0x000000010338277b
(lldb) continue
Process 9532 resuming

(make sure the breakpoint is triggered)
Process 9532 resuming
Process 9532 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x00007fff817becfb AppKit`-[NSView hitTest:]
AppKit`-[NSView hitTest:]:
->  0x7fff817becfb <+0>: pushq  %rbp
    0x7fff817becfc <+1>: movq   %rsp, %rbp
    0x7fff817becff <+4>: pushq  %r15
    0x7fff817bed01 <+6>: pushq  %r14

# po is usually more useful as it gives the NSObject’s description or debugDescription, if available.
(lldb) po $rdi
<_NSThemeCloseWidget: 0x11ca41f80>

# 给断点添加条件
(lldb) breakpoint modify 1 -c "(BOOL)[$rdi isKindOfClass:[NSTextView class]]"
(触发断点后)
(lldb) po [$rdi superclass]

# 用lldb查看私有API r-使用正则表达式;n-通过名字寻找函数或符号
(lldb) image lookup -rn 'DVTSourceTextView\ '

# 通过block注入来swizzle
(lldb) po @import Foundation
(lldb) po
Enter expressions, then terminate with an empty line to evaluate:
@import Cocoa;
id $class = [NSObject class];
SEL $sel = @selector(init);
void *$method = (void *)class_getInstanceMethod($class, $sel);
IMP $oldImp = (IMP)method_getImplementation($method);
id (^$block)(id) = ^id(id object) {
  if ((BOOL)[object isKindOfClass:[NSView class]]) {
    fprintf(stderr, "%s\n", (char *)[[[object class] description]
UTF8String]);
  }
  return object;
};
IMP $newImp = (IMP)imp_implementationWithBlock($block);
method_setImplementation($method, $newImp);

# 善于用help和apropos查看文档
(lldb) help breakpoint name
# will do a case-insensitive search for any word or string against the LLDB documentation and return any matching results.
(lldb) apropos swift

Chapter 3: Attaching with LLDB

LLDB attaching实际是个误导人的词。debugserver才是真正负责attaching到进程的。

# attach到已存在进程
$ lldb -n Xcode
$ lldb -p `pgrep -x Xcode`

# attach到未存在进程
$ lldb -n Finder -w  # LLDB会等待直到一个叫Finder的进程下次启动

# attach到自己想要的进程。这种方式有个弊端是进程的stderr output (i.e. NSLog & company)回自动发送到Terminal里面去。
$ lldb -f /System/Library/CoreServices/Finder.app/Contents/MacOS/Finder
(lldb) process launch

# Options while launching
$ lldb -f /bin/ls
(lldb) process launch -w /Applications   # 改变当前工作目录,等于cd再ls
(lldb) process launch -- /Applications  # 给ls传参,等于 ls /Applications
(lldb) process launch -- ~/Desktop  # ls会报错,这个目录不存在
(lldb) process launch -X true -- ~/Desktop  # 用-X true来展开任何shell符号
(lldb) run ~/Desktop  # run 等价于 process launch -X true --
(lldb) process launch -o /tmp/ls_output.txt -- /Applications

(lldb) target delete
(lldb) target create /usr/bin/wc
$ echo "hello world" > /tmp/wc_input.txt
(lldb) process launch -i /tmp/wc_input.txt  # 等价于 wc < /tmp/wc_input.txt

(lldb) run  # 不想要stdin,程序会一直在那等着,输入内容,Ctrl+D结束输入

(lldb) process launch -n  # -n告诉LLDB不创建stdin,于是wc会立即结束突出

Chapter 4: Stopping in Code

# 检查一个函数是否存在
(lldb) image lookup -n "-[UIViewController viewDidLoad]"
1 match found in /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk//System/Library/Frameworks/UIKit.framework/UIKit:
        Address: UIKit[0x00000000001ca654] (UIKit.__TEXT.__text + 1867588)
        Summary: UIKit`-[UIViewController viewDidLoad]

(lldb) image lookup -rn test  # 正则查找在任何包含有test(大小写匹配)的函数或框架

# 然后添加断点
(lldb) b -[UIViewController viewDidLoad]
(lldb) rb '\-\[UIViewController\ '  # rb = breakpoint set -r %1
(lldb) breakpoint delete  # 删除所有断点
(lldb) rb '\-\[UIViewController(\(\w+\))?\ '  # 找出包括category方法的有所UIVC的实例方法
(lldb) rb . -f DetailViewController.swift  # -f域限制,这个为所有的属性getter/setter,block,category,方法或函数添加断点
(lldb) rb .  # 这个是非常疯狂的,为所有一切添加断点
(lldb) rb . -s UIKit  # 限制在某个library,为这个库的所有东西添加断点
(lldb) rb . -s UIKit -o  # 断点只会被触发一次然后被删除

修改或删除断点

(lldb) b main
  Breakpoint 1: 20 locations.
(lldb) breakpoint list
(lldb) breakpoint list 1
(lldb) breakpoint list 1 -b
(lldb) breakpoint list 1 3
(lldb) breakpoint list 1-3  # 以上也适用delete
(lldb) breakpoint delete 1
(lldb) breakpoint delete 1,1

Chapter 5: Expression

关于expression命令的介绍,用它你可以在debugger中执行任意代码

Formatting p & po

'p' = 'expression --'
'po' = 'expression -O --' # -O用来打印object的description

(lldb) p self
(Signals.MasterViewController) $R3 = 0x00007fc08860e960 {
  UIKit.UITableViewController = {
    baseUIViewController@0 = <extracting data from value failed>

    _tableViewStyle = 0
    _keyboardSupport = nil
    _staticDataSource = nil
    _filteredDataSource = 0x0000608000245e20
    _filteredDataType = 0
  }
  detailViewController = nil
}
(lldb) type summary add Signals.MasterViewController --summary-string "Wahoo!"
(lldb) p self
(Signals.MasterViewController) $R5 = 0x00007fc08860e960 Wahoo!
(lldb) type summary clear

Swift vs Objective-C debugging contexts

调试oc代码时,lldb便是oc语法;调试swift代码时,lldb便是swift语法。
有个例外时非断点停止而是主动点击暂停按钮(pause the application out of the blue),进入的context一定是OC,不管app是不是swift

# 在swift环境强制使用oc语法
(lldb) expression -l objc -O -- [UIApplication sharedApplication]

User defined variables

一定要带上美元符号
oc环境创建的变量,最好还是不要用于swift环境,行为是未定义的,Apple的开发者正在开发这个语境

(lldb) po id $test = [NSObject new]
(lldb) po $test
<NSObject: 0x60000001d190>
(lldb) expression -l swift -O -- $test
<NSObject: 0x60000001d190>
(lldb) exppression -l swift -O -- $test.description
error: <EXPR>:3:1: error: use of unresolved identifier '$test'
$test.description
^~~~~

# 随便在一个类的实例方法里面设置一个断点(最好是ViewController的,这样易于观察实验结果),run并命中断点
(lldb) p self  # lldb会赋予一个变量 $R0 引用它,请恢复执行,并手动停止
(lldb) po $R0.title  # 记住上面的内容,主动停止会进入oc环境
error: use of undeclared identifier '$R0'
(lldb) expression -l swift -- $R0.title
(String?) $R1 = "Quarterback"  # 会打印出ViewController的title!
(lldb) expression -l swift -- $R0.title = "💩💩💩💩💩 "  # ⌘ + ⌃ + space然后输入poop可以找到这个emoji
# 恢复执行你就可以看到ViewController的title变成五坨翔啦。这个东西特别好用,当你需要观察给一个方法传一些特殊的值观察对应行为的时候

# 假设上面你下的断点在viewDidAppear函数里,暂停执行
(lldb) expression -l swift -O -- $R0.viewDidLoad()  
# 你会发现断点并没有命中!这是因为lldb默认忽视任何断点。你可以通过-i来打开
(lldb) expression -l swift -O -i 0 -- $R0.viewDidLoad()
# 现在断点可以正常的被触发了。这个技巧是一个很好的用来测试函数的方法。比如,你可以实现test-driven debugging,通过给一个函数传递不同的参数来观察它是如何处理不同的输入

Type formatting类型格式化

LLDB有一个很不错的功能是格式化基本类型数据,这使得LLDB是一个很好的

# 主动停止app
(lldb) expression -G x -- 10  # G代表GDB
(int) $0 = 0x0000000a
(lldb) p/x 10  # /x 指定用十六进制格式
(int) $1 = 0x0000000a
(lldb) p/t 10  # /t 指定用二进制格式
(int) $2 = 0b00000000000000000000000000001010
(lldb) p/t -10
(int) $3 = 0b11111111111111111111111111110110
(lldb) p/t 10.0
(double) $4 = 0b0100000000100100000000000000000000000000000000000000000000000000
(lldb) p/d 'D'  # /d 指定用十进制格式
(char) $5 = 68
(lldb) p/c 1430672467  # /c 指定用char格式
(int) $6 = STFU  # 将这个整型转换成二进制,并分成4个byte,把每个byte转换成ASCII码

完整的输出格式化列表如下:
• x: hexadecimal
• d: decimal
• u: unsigned decimal
• o: octal
• t: binary
• a: address
• c: character constant
• f: float
• s: string
如果这些格式化还不能够满足你,你可以使用lldb额外的formatters,但此时你不可以使用GDB的格式化语法了:

(lldb) expression -f Y -- 1430672467
(int) $0 = 53 54 46 55             STFU

Chapter 6: Thread, Frame & Stepping Around

一个神奇的方法:Control+Shift+StepOver/StepIn
可以控制其他线程保持停止状态,这个在排查棘手的同步问题时特别有用

(lldb) thread backtrace
(lldb) bt  # 这两个其实是不一样的,用help验证
(lldb) frame info  # 打印出当前frame
frame #0: 0x000000010f5ce87a Signals`MasterViewController.viewWillAppear(animated=false, self=0x00007fcb3651be10) -> () at MasterViewController.swift:55

(lldb) frame select 1
frame #1: 0x000000010f5cee51 Signals`@objc MasterViewController.viewWillAppear(Bool) -> () at MasterViewController.swift:0
   1    /**
   2     * MasterViewController.swift
   3     *
   4     * Copyright (c) 2017 Razeware LLC
   5     *
   6     * Permission is hereby granted, free of charge, to any person obtaining a copy
   7     * of this software and associated documentation files (the "Software"), to deal
(lldb) run  # 重新启动app,Xcode不需要重编译
(lldb) next  # step over
(lldb) step  # step into Xcode默认next如果没有debug symbols
(lldb) settings show target.process.thread.step-in-avoid-nodebug
target.process.thread.step-in-avoid-nodebug (boolean) = true
(lldb) step -a0  # 忽视设置,不管有无debug symbols都进入
(lldb) finish  # step out 回车lldb会执行上一条指令
(lldb) next/step --run-mode/-m  # 控制其余线程是否停止

查看栈上数据

一个非常有用的frame子命令是frame variable。这个命令会用在可执行文件头部中(或dSYM如果你的app被stripped了)的debug symbol信息来dump出关于那个栈帧的信息。有了这个debug信息,这个命令只要带上恰当的参数,你很容易就知道函数中所有变量(包括程序中的全局变量)的作用域

(lldb) frame variable  # 查看当前栈帧的变量
(Bool) animated = false
(Signals.MasterViewController) self = 0x00007f94de713760 {
  UIKit.UITableViewController = {
    baseUIViewController@0 = <extracting data from value failed>

    _tableViewStyle = 0
    _keyboardSupport = 0x000060800002aa00
    _staticDataSource = nil
    _filteredDataSource = 0x000060800005bcc0
    _filteredDataType = 0
  }
  detailViewController = nil
}
(CGFloat) bottomInset = 4.9406564584124654E-324
(lldb) frame info


  (lldb) frame variable -F self  # 查看所有私有变量,F是flat的意思

Chapter 7: Image

block所在函数的参数是如何传给block的呢?
编译器能够分析出哪些参数是block需要的,然后创建一个以这些参数为参数的函数。但block被调用时,实际上是这个函数被调用了。

(lldb) image list
(lldb) image list Foundation
(lldb) image dump symtab UIKit -s address  # dump all the symbol table informaton available for UIKit
(lldb) image lookup -n "-[UIViewController viewDidLoad]"
1 match found in /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk//System/Library/Frameworks/UIKit.framework/UIKit:
        Address: UIKit[0x00000000001ca9a4] (UIKit.__TEXT.__text + 1868340)
        Summary: UIKit`-[UIViewController viewDidLoad]
(lldb) image lookup -rn UIViewController
(lldb) image lookup -rn '\[UIViewController\ '
(lldb) image lookup -rn '\[UIViewController\(\w+\)\ '  # 包括category的方法
(lldb) image lookup -rn _block_invoke
(lldb) image lookup -rn _block_invoke ImageName

# 给一个block下断点,观察block的frame variable
(lldb) frame variable
(__block_literal_5 *)  = 0x0000600000269c00
(int) sig = 23
(siginfo_t *) siginfo = 0x00007fff55a67588
(UnixSignalHandler *) self = 0x0000608000262c80
(UnixSignal *) unixSignal = 0x000000010eb3cd08
(__NSCFConstantString *) kSignalHandlerCountUpdatedNotification = 0x000000010a25e488 @"com.razeware.breakpoints.contentupdated"
(lldb) image dump symfile ImageName
# 很多内容出来,文本查找__block_literal_5
0x7fb975802760:   Type{0x100000ce6} , name = "__block_literal_5", size = 52, decl = UnixSignalHandler.m:123, compiler_type = 0x00007fb979082860 struct __block_literal_5 {
        void *__isa;
        int __flags;
        int __reserved;
        void (*__FuncPtr)();
        __block_descriptor_withcopydispose *__descriptor;
        UnixSignalHandler *const self;
        siginfo_t *siginfo;
        int sig;
    }
这个就是定义了block的东东啦
(lldb) po (__block_literal_5 *)0x0000600000269c00
<__NSMallocBlock__: 0x600000269c00>
(lldb) p/x ((__block_literal_5 *)0x0000600000269c00)->__FuncPtr
(void (*)()) $2 = 0x000000010a259810 (Commons`__38-[UnixSignalHandler appendSignal:sig:]_block_invoke_2 at UnixSignalHandler.m:123)
(lldb) image lookup -a 0x000000010a259810
      Address: Commons[0x0000000000002810] (Commons.__TEXT.__text + 2352)
      Summary: Commons`__38-[UnixSignalHandler appendSignal:sig:]_block_invoke_2 at UnixSignalHandler.m:123
(lldb) po ((__block_literal_5 *)0x0000600000269c00)->sig
23

使用image dump symfile ImageName命令来探究一个未知的数据类型是如何工作的,这是一个很好的手段。这个命令也可以让我们了解编译器如何生成目标代码。除此之外,你也可以通过它来检查block是怎么引用block外部的指针来调试内存循环引用问题。

(lldb) image lookup -rn __NSMallocBlock__  # 没有任何输出,说明它没有重写父类的任何方法
(lldb) po [__NSMallocBlock__ superclass]
__NSMallocBlock
(lldb) image lookup -rn __NSMallocBlock  # 输出的方法看起来都和内存管理有关
(lldb) po [__NSMallocBlock superclass]
NSBlock
(lldb) image lookup -rn 'NSBlock\ '
6 matches found in /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk//System/Library/Frameworks/CoreFoundation.framework/CoreFoundation:
        Address: CoreFoundation[0x00000000000400c0] (CoreFoundation.__TEXT.__text + 257696)
        Summary: CoreFoundation`-[NSBlock copy]        Address: CoreFoundation[0x0000000000078330] (CoreFoundation.__TEXT.__text + 487696)
        Summary: CoreFoundation`-[NSBlock copyWithZone:]        Address: CoreFoundation[0x00000000001892b0] (CoreFoundation.__TEXT.__text + 1605776)
        Summary: CoreFoundation`+[NSBlock allocWithZone:]        Address: CoreFoundation[0x00000000001892d0] (CoreFoundation.__TEXT.__text + 1605808)
        Summary: CoreFoundation`+[NSBlock alloc]        Address: CoreFoundation[0x00000000001892f0] (CoreFoundation.__TEXT.__text + 1605840)
        Summary: CoreFoundation`-[NSBlock invoke]        Address: CoreFoundation[0x0000000000189300] (CoreFoundation.__TEXT.__text + 1605856)
        Summary: CoreFoundation`-[NSBlock performAfterDelay:]
# 接下来我们想探究下invoke这个方法,但是我们调用invoke之后不希望这个block被内存管理给释放掉。可以这样处理
(lldb) po id $block = (id)0x0000600000269c00
(lldb) po [$block retain]
<__NSMallocBlock__: 0x600000269c00>
(lldb) po [$block invoke]
2017-07-11 10:38:05.075 Signals[507:5687492] Appending new signal: SIGIO

(lldb) image lookup -rn (?i)\ _\w+description\]  # (?i) means case insensitive
(lldb) image lookup -rn NSObject\(IvarDescription\)
这3个方法非常吸引人:
_ivarDescription
_propertyDescription 
_methodDescription
(lldb) po [[UIApplication sharedApplication] _ivarDescription]  # 可以发现一个叫UIStatusBar的私有类
(lldb) image lookup -rn '\[UIStatusBar\ set'
(lldb) po [[UIApplication sharedApplication] statusBar]
<UIStatusBar: 0x7fcb42809e00; frame = (0 0; 414 20); opaque = NO; autoresize = W+BM; layer = <CALayer: 0x608000032400>>
(lldb) po [0x7fcb42809e00 setBackgroundColor:[UIColor purpleColor]]  # statusBar就会变成紫色的啦

Chapter 8: Persisting & Customizing Commands 持久化和自定义命令

如何持久化

LLDB启动时会在几个目录里面寻找初始化文件,如果找到则会被加载到LLDB中,加载时机是在LLDB启动后attach到process之前。
LLDB将会在以下几个地方寻找初始化文件:
1.~/.lldbinit-[context]。其中context为Xcode或者lldb。即如果只想生效于Xcode中的lldb,用~/.lldbinit-Xcode,如果只想生效与命令行中的lldb,用~/.lldbinit-lldb

  1. 接下来LLDB就会搜索在~/.lldbinit-[context]中找到的内容。这个是最理想的文件,大部分情形我们还是希望两种场景下都使用
  2. LLDB将会搜索被启动时的目录。不幸的是,Xcode启动LLDB在/目录

创建.lldbinit文件

# 注意这里的lldb context应该是Swift,所以才强制为oc语法
(lldb) command alias -- Yay_Autolayout expression -l objc -O -- [[[[[UIApplication sharedApplication] keyWindow] rootViewController] view] recursiveDescription]
(lldb) command alias cpo expression -l objc -O --

Chapter 9: Regex Commands

alias只能方便我们处理静态的命令,如果命令有接受输入的话alias就显得很捉急了。

# 对新命令后面的内容进行正则替换。下面这个命令alias是无法完成的
(lldb) command regex rlook 's/(.+)/image lookup -rn %1/'  # %1指的是被匹配的内容
(lldb) rlook F00  # 等同于 image lookup -rn FOO, 正则表达式是从rlook之后开始匹配
(lldb) rl viewDidLoad  # 等同于 rlook viewDidLoad
(lldb) rl viewDidLoad Signals
# 来点高级点的
(lldb) command regex -- tv 's/(.+)/expression -l objc -O -- @import QuartzCore; [%1 setHidden:!(BOOL)[%1 isHidden]]; (void)[CATransaction flush];/'
(lldb) tv [[[UIApp keyWindow] rootViewController] view]  # 重复这个命令,手机就会黑屏、恢复黑屏一直循环

(lldb) command regex getcls 's/(([0-9]|\$|\@|\[).*)/cpo [%1 class]/'
(lldb) getcls @"hello world"
__NSCFString
(lldb) getcls @[@"hello world"] 
__NSSingleObjectArrayI
(lldb) getcls [UIDevice currentDevice] 
UIDevice
(lldb) cpo [UIDevice currentDevice] 
<UIDevice: 0x60800002b520>
(lldb) getcls 0x60800002b520
UIDevice
(lldb) command regex getcls 's/(([0-9]|\$|\@|\[).*)/cpo [%1 class]/' 's/(.+)/expression -l swift -O -- type(of: %1)/'
(lldb) getcls self
Signals.MasterViewController
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,050评论 4 370
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,538评论 1 306
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,673评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,622评论 0 218
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,047评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,974评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,129评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,893评论 0 209
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,654评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,828评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,297评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,619评论 3 262
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,326评论 3 243
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,176评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,975评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,118评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,909评论 2 278

推荐阅读更多精彩内容

  • 嗯哼嗯哼蹦擦擦~~~ 转载自:https://github.com/Tim9Liu9/TimLiu-iOS 目录 ...
    philiha阅读 4,663评论 0 6
  • 转载 与调试器共舞 - LLDB 的华尔兹: https://objccn.io/issue-19-2/ 推荐:i...
    F麦子阅读 3,285评论 0 10
  • 来时的夜晚,仿佛就在昨天。今天的我已踏上开往沈阳的列车,不得不感慨时间的流逝…望着窗外的自己,家的片段随车窗...
    简镰阅读 184评论 0 0
  • 女人身上看男人,真的很准,值得一看! 2017-03-18沟通技巧 此文送给结过婚、离过婚、想离婚、没离婚、想结婚...
    ZhouWG阅读 326评论 0 1
  • ~1998.
    罗少先阅读 223评论 0 0