[翻譯]用 LLDB 調(diào)試 Swift 代碼

用 LLDB 調(diào)試 Swift 代碼

作為工程師正什,我們花了差不多 70% 的時間在調(diào)試上,剩下的 20% 用來思考架構(gòu)以及和組員溝通凭舶,僅僅只有 10% 的時間是真的在寫代碼的又沾。

調(diào)試就像是在犯罪電影中做偵探一樣罐盔,同時你也是兇手篮赢。

Filipe Fortes 來自 Twitter

所以讓我們在這70%的時間盡可能愉悅是相當(dāng)重要的奏甫。LLDB 就是來打救我們的。奇妙的 Xcode Debugger UI 展示了所有你可用的信息症副,而不用敲入任何一個 LLDB 命令店雅。然而,控制臺在我們的工作中同樣也是很重要的一部分⊥吒猓現(xiàn)在讓我們來分析一些最有用的 LLDB 技巧底洗。我自己每天都在用它們進(jìn)行調(diào)試腋么。

從哪里開始呢咕娄?

LLDB 是一個龐大的工具,內(nèi)置了很多有用的命令珊擂。我不會全部講解圣勒,而是帶你瀏覽最有用的命令费变。這是我們的計劃:

  1. 獲取變量值: expression, e, print, po, p
  2. 獲取整個應(yīng)用程序的狀態(tài)以及特定語言的命令:bugreport, frame, language
  3. 控制應(yīng)用的執(zhí)行流程:process, breakpoint, thread, watchpoint
  4. 榮譽(yù)獎:command, platform, gui

我還準(zhǔn)備好了有用的 LLDB 命令說明和實例的表格,有需要的可以把它貼在 Mac 上面記住這些命令 ??

通過這條鏈接下載全尺寸的版本 — ?https://www.dropbox.com/s/9sv67e7f2repbpb/lldb-commands-map.png?dl=0

1. 獲取變量值和狀態(tài)

命令:expression, e, print, po, p

調(diào)試器的一個基礎(chǔ)功能就是獲取和修改變量的值圣贸。這就是 expression 或者 e 被創(chuàng)造的原因(當(dāng)然他們還有更高級的功能)挚歧。您可以簡單的在運(yùn)行時執(zhí)行任何表達(dá)式或命令。

假設(shè)你現(xiàn)在正在調(diào)試方法 valueOfLifeWithoutSumOf() :對兩個數(shù)求和吁峻,再用42去減得到結(jié)果滑负。

繼續(xù)假設(shè)你一直得到錯誤的結(jié)果并且你并不知道是什么原因。所以你可以做以下的事來找到問題:

或者用含。矮慕。。使用 LLDB 表達(dá)式在運(yùn)行時修改值才是更好的方法啄骇,同時可以找出問題是在哪里出現(xiàn)的痴鳄。首先,在你感興趣的地方設(shè)置一個斷點缸夹,然后運(yùn)行你的應(yīng)用痪寻。

為了用 LLDB 格式打印指定的變量你應(yīng)該調(diào)用:

(lldb) e <variable>

使用相同的命令來執(zhí)行一些表達(dá)式:

(lldb) e <expression>
(lldb) e sum 
(Int) $R0 = 6 // 下面你也可以用 $R0 來引用這個變量(在本次調(diào)試過程中)

(lldb) e sum = 4 // 修改變量 sum 的值

(lldb) e sum 
(Int) $R2 = 4 // 直到本次調(diào)試結(jié)束變量 sum 都會是 "4" 

expression 命令也有一些標(biāo)志。在 expression 后面用雙破折號 -- 將標(biāo)志和實際的表達(dá)式分隔開虽惭,就像這樣:

(lldb) expression <some flags> -- <variable>

expression 命令差不多有30種不同的標(biāo)志橡类。我鼓勵你多去探索它們。在終端中鍵入以下命令可以看到完整的文檔:

> lldb
> (lldb) help # 獲取所有變量的命令
> (lldb) help expression # 獲取所有表達(dá)式的子命令

我會在下列 expression 的標(biāo)志上多停留一會兒:

  • -D <count> (--depth <count>)? — 設(shè)置在轉(zhuǎn)儲聚合類型時的最大遞歸深度(默認(rèn)為無窮大)芽唇。
  • -O (--object-description)? — 如果可能的話猫态,使用指定語言的描述API來顯示。
  • -T (--show-types)? — 在轉(zhuǎn)儲值的時候顯示變量類型披摄。
  • -f <format> (--format <format>) — 指定一種用于顯示的格式亲雪。
  • -i <boolean> (--ignore-breakpoints <boolean>)?— 在運(yùn)行表達(dá)式時忽略斷點。

假設(shè)我們有一個叫 logger 的對象疚膊,這個對象有一些字符串和結(jié)構(gòu)體類型的屬性义辕。比如說,你可能只是想知道第一層的屬性寓盗,那只需要用 -D 標(biāo)志以及恰當(dāng)?shù)膶蛹壣疃戎倒嘧拖襁@樣:

(lldb) e -D 1 -- logger

(LLDB_Debugger_Exploration.Logger) $R5 = 0x0000608000087e90 {
  currentClassName = "ViewController"
  debuggerStruct ={...}
}

默認(rèn)情況下,LLDB 會無限地遍歷該對象并且給你展示每個嵌套的對象的完整描述:

(lldb) e -- logger

(LLDB_Debugger_Exploration.Logger) $R6 = 0x0000608000087e90 {
  currentClassName = "ViewController"
  debuggerStruct = (methodName = "name", lineNumber = 2, commandCounter = 23)
}

你也可以用 e -O -- 獲取對象的描述或者更簡單地用別名 po傀蚌,就像下面的示例一樣:

(lldb) po logger

<Logger: 0x608000087e90>

并不是很有描述性基显,不是嗎?為了獲取更加可閱讀的描述善炫,你自定義的類必須遵循 CustomStringConvertible 協(xié)議撩幽,同時實現(xiàn) var description: String { return ...} 屬性。接下來只需要用 po 就能返回可讀的描述。

在本節(jié)的開始窜醉,我也提到了 print 命令宪萄。基本上 print <expression/variable> 就等同于 expression -- <expression/variable>榨惰。但是 print 命令不能帶任何標(biāo)志或者額外的參數(shù)拜英。

2. 獲取整個 APP 的狀態(tài)和指定語言的命令

bugreport, frame, language

你是否經(jīng)常復(fù)制粘貼崩潰日志到任務(wù)管理器中方便稍后能考慮這個問題嗎?LLDB 提供了一個很好用的命令叫 bugreport琅催,這個命令能生成當(dāng)前應(yīng)用狀態(tài)的完整報告居凶。在你偶然觸發(fā)某些問題但是想在稍后再解決它時這個命令就會很有幫助了。為了能恢復(fù)應(yīng)用的狀態(tài)藤抡,你可以使用 bugreport 生成報告排监。

(lldb) bugreport unwind --outfile <path to output file>

最終的報告看起來就像下面截圖中的例子一樣:


bugreport 命令輸出的示例。

假設(shè)你想要獲取當(dāng)前線程的當(dāng)前棧幀的概述杰捂,frame 命令可以幫你完成:

使用下面的代碼片段來快速獲取當(dāng)前地址以及當(dāng)前的環(huán)境條件:

(lldb) frame info

frame #0: 0x000000010bbe4b4d LLDB-Debugger-Exploration`ViewController.valueOfLifeWithoutSumOf(a=2, b=2, self=0x00007fa0c1406900) -> Int at ViewController.swift:96

這些信息在本文后面將要說到的斷點管理中非常有用舆床。

LLDB 有幾個指定語言的命令,包括C++嫁佳,Objective-C挨队,Swift 和 RenderScript。在這篇文章中蒿往,我們重點關(guān)注 Swift盛垦。這是兩個命令:demanglerefcount

demangle 正如其名字而言瓤漏,就是用來重組 Swift 類型名的(因為 Swift 在編譯的時候會生成類型名來避免命名空間的問題)腾夯。如果你想了解多一點的話,我建議你看 WWDC14 的這個分享會 — ?“Advanced Swift Debugging in LLDB”蔬充。

refcount 同樣也是一個相當(dāng)直觀的命令蝶俱,能獲得指定對象的引用數(shù)量。一起來看一下對象輸出的示例饥漫,我們用了上一節(jié)講到的對象 — logger

(lldb) language swift refcount logger

refcount data: (strong = 4, weak = 0)

當(dāng)然了榨呆,在你調(diào)試某些內(nèi)存泄露問題時,這個命令就會很有幫助庸队。

3. 控制應(yīng)用的執(zhí)行流程

process, breakpoint, thread

這節(jié)是我最喜歡的一節(jié)积蜻,因為在 LLDB 使用這幾個命令(尤其是 breakpoint 命令),你可以在調(diào)試的時候使很多常規(guī)任務(wù)變得自動化彻消,這樣就能大大加快你的調(diào)試工作竿拆。

通過 process 基本上你就可以控制調(diào)試的過程了,還能鏈接到特定的 target 或者停止調(diào)試器宾尚。 但是因為 Xcode 已經(jīng)自動地幫我們做好了這個工作了(Xcode 在任何時候運(yùn)行一個 target 時都會連接 LLDB)丙笋。我不會在這兒講太多,你可以在這篇 Apple 的指南中閱讀一下如何用終端連接到一個 target — “Using LLDB as a Standalone Debugger”洼畅。

使用 process status 的話枫振,你可以知道當(dāng)前調(diào)試器停住的地址:

(lldb) process status

Process 27408 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
frame #0: 0x000000010bbe4889 LLDB-Debugger-Exploration`ViewController.viewDidLoad(self=0x00007fa0c1406900) -> () at ViewController.swift:69
66
67           let a = 2, b = 2
68           let result = valueOfLifeWithoutSumOf(a, and: b)
-> 69           print(result)
70
71
72

想要繼續(xù) target 的執(zhí)行過程直到遇到下次斷點的話,運(yùn)行這個命令:

(lldb) process continue

(lldb) c // 或者只鍵入 "c",這跟上一條命令是一樣的

這個命令等同于 Xcode 調(diào)試器工具欄上的”continue“按鈕:

breakpoint 命令允許你用任何可能的方式操作斷點稳吮。我們跳過最顯而易見的命令:breakpoint enable, breakpoint disablebreakpoint delete

首先井濒,查看你所有斷點的話可以用如下示例中的 list 子命令:

(lldb) breakpoint list

Current breakpoints:
1: file = '/Users/Ahmed/Desktop/Recent/LLDB-Debugger-Exploration/LLDB-Debugger-Exploration/ViewController.swift', line = 95, exact_match = 0, locations = 1, resolved = 1, hit count = 1

1.1: where = LLDB-Debugger-Exploration`LLDB_Debugger_Exploration.ViewController.valueOfLifeWithoutSumOf (Swift.Int, and : Swift.Int) -> Swift.Int + 27 at ViewController.swift:95, address = 0x0000000107f3eb3b, resolved, hit count = 1

2: file = '/Users/Ahmed/Desktop/Recent/LLDB-Debugger-Exploration/LLDB-Debugger-Exploration/ViewController.swift', line = 60, exact_match = 0, locations = 1, resolved = 1, hit count = 1

2.1: where = LLDB-Debugger-Exploration`LLDB_Debugger_Exploration.ViewController.viewDidLoad () -> () + 521 at ViewController.swift:60, address = 0x0000000107f3e609, resolved, hit count = 1

列表中的第一個數(shù)字是是斷點的 ID灶似,你可以通過這個 ID 引用到指定的斷點。現(xiàn)在讓我們在控制臺中設(shè)置一些新的斷點:

(lldb) breakpoint set -f ViewController.swift -l 96

Breakpoint 3: where = LLDB-Debugger-Exploration`LLDB_Debugger_Exploration.ViewController.valueOfLifeWithoutSumOf (Swift.Int, and : Swift.Int) -> Swift.Int + 45 at ViewController.swift:96, address = 0x0000000107f3eb4d

這個例子中的 -f 是你想要放置斷點處的文件名瑞你,-l 是新斷點的行數(shù)酪惭。還有一種更簡潔的方式設(shè)置同樣的斷點,就是用快捷方式 b

(lldb) b ViewController.swift:96

同樣地者甲,你也可以用指定的正則(比如函數(shù)名)來設(shè)置斷點春感,使用下面的命令:

(lldb) breakpoint set --func-regex valueOfLifeWithoutSumOf

(lldb) b -r valueOfLifeWithoutSumOf // 上一條命令的簡化版本

有些時候設(shè)置斷點只命中一次也是有用的,然后指示這個斷點立即刪除自己虏缸,當(dāng)然啦鲫懒,有一個命令來處理這件事:

(lldb) breakpoint set --one-shot -f ViewController.swift -l 90

(lldb) br s -o -f ViewController.swift -l 91 // 上一條命令的簡化版本

現(xiàn)在我們來到了最有趣的部分 — 自動化斷點。你知道你可以設(shè)置一個特定的動作使它在斷點停住的時候執(zhí)行嗎刽辙?是的窥岩,你可以!你是否會在代碼中用 print() 來在調(diào)試的時候得到你感興趣的值宰缤?請不要再這樣做了颂翼,這里有一種更好的方法。??

通過 breakpoint 命令慨灭,你可以設(shè)置好命令朦乏,使其在斷點命中時可以正確執(zhí)行。你甚至可以設(shè)置”不可見“的斷點氧骤,這種斷點并不會打斷運(yùn)行過程集歇。從技術(shù)上講,這些“不可見的”斷點其實是會中斷執(zhí)行的语淘,但如果在命令鏈的末尾添上“continue”命令的話诲宇,你就不會注意到它。

(lldb) b ViewController.swift:96 // Let's add a breakpoint first

Breakpoint 2: where = LLDB-Debugger-Exploration`LLDB_Debugger_Exploration.ViewController.valueOfLifeWithoutSumOf (Swift.Int, and : Swift.Int) -> Swift.Int + 45 at ViewController.swift:96, address = 0x000000010c555b4d

(lldb) breakpoint command add 2 // 準(zhǔn)備某些命令

Enter your debugger command(s).  Type 'DONE' to end.
> p sum // 打印變量 "sum" 的值
> p a + b // 運(yùn)行 a + b
> DONE

為了確保你添加的命令是正確的惶翻,可以使用 breakpoint command list <breakpoint id> 子命令:

(lldb) breakpoint command list 2

Breakpoint 2:
Breakpoint commands:
p sum
p a + b

當(dāng)下次斷點命中時我們就會在控制臺看到下面的輸出:

Process 36612 resuming
p sum
(Int) $R0 = 6

p a + b
(Int) $R1 = 4

太棒了姑蓝!這正是我們想要的。你可以通過在命令鏈的末尾添加 continue 命令讓執(zhí)行過程更加順暢吕粗,這樣你就不會停在這個斷點纺荧。

(lldb) breakpoint command add 2 // 準(zhǔn)備某些命令

Enter your debugger command(s).  Type 'DONE' to end.
> p sum // 打印變量 "sum" 的值
> p a + b // 運(yùn)行 a + b
> continue // 第一次命中斷點后直接恢復(fù)
> DONE

結(jié)果會是這樣:

p sum
(Int) $R0 = 6

p a + b
(Int) $R1 = 4

continue
Process 36863 resuming
Command #3 'continue' continued the target.

通過 thread 命令和它的子命令,你可以完全操控執(zhí)行流程:step-over, step-in, step-outcontinue。這些命令等同于 Xcode 調(diào)試器工具欄上的流程控制按鈕宙暇。

LLDB 同樣也對這些特殊的命令預(yù)先定義好了快捷方式:

(lldb) thread step-over
(lldb) next // 和 "thread step-over" 命令效果一樣
(lldb) n // 和 "next" 命令效果一樣

(lldb) thread step-in
(lldb) step // 和 "thread step-in" 命令效果一樣
(lldb) s // 和 "step" 命令效果一樣

為了獲取當(dāng)前線程的更多信息输枯,我們只需要調(diào)用 info 子命令:

(lldb) thread info 

thread #1: tid = 0x17de17, 0x0000000109429a90 LLDB-Debugger-Exploration`ViewController.sumOf(a=2, b=2, self=0x00007fe775507390) -> Int at ViewController.swift:90, queue = 'com.apple.main-thread', stop reason = step in

想要看到當(dāng)前所有的活動線程的話使用 list 子命令:

(lldb) thread list

Process 50693 stopped

* thread #1: tid = 0x17de17, 0x0000000109429a90 LLDB-Debugger-Exploration`ViewController.sumOf(a=2, b=2, self=0x00007fe775507390) -> Int at ViewController.swift:90, queue = 'com.apple.main-thread', stop reason = step in

  thread #2: tid = 0x17df4a, 0x000000010daa4dc6  libsystem_kernel.dylib`kevent_qos + 10, queue = 'com.apple.libdispatch-manager'
  
  thread #3: tid = 0x17df4b, 0x000000010daa444e libsystem_kernel.dylib`__workq_kernreturn + 10

  thread #5: tid = 0x17df4e, 0x000000010da9c34a libsystem_kernel.dylib`mach_msg_trap + 10, name = 'com.apple.uikit.eventfetch-thread'

榮譽(yù)獎

command, platform, gui

在 LLDB 中你可以找到一個命令管理其他的命令,聽起來很奇怪占贫,但實際上它是非常有用的小工具桃熄。首先,它允許你從文件中執(zhí)行一些 LLDB 命令型奥,這樣你就可以創(chuàng)建一個儲存著一些實用命令的文件瞳收,然后就能立刻允許這些命令,就像是單個命令那樣厢汹。這是所說的文件的簡單例子:

thread info // 顯示當(dāng)前線程的信息
br list // 顯示所有的斷點

下面是實際命令的樣子:

(lldb) command source /Users/Ahmed/Desktop/lldb-test-script

Executing commands in '/Users/Ahmed/Desktop/lldb-test-script'.

thread info
thread #1: tid = 0x17de17, 0x0000000109429a90 LLDB-Debugger-Exploration`ViewController.sumOf(a=2, b=2, self=0x00007fe775507390) -> Int at ViewController.swift:90, queue = 'com.apple.main-thread', stop reason = step in

br list
Current breakpoints:
1: file = '/Users/Ahmed/Desktop/Recent/LLDB-Debugger-Exploration/LLDB-Debugger-Exploration/ViewController.swift', line = 60, exact_match = 0, locations = 1, resolved = 1, hit count = 0
1.1: where = LLDB-Debugger-Exploration`LLDB_Debugger_Exploration.ViewController.viewDidLoad () -> () + 521 at ViewController.swift:60, address = 0x0000000109429609, resolved, hit count = 0

遺憾的是還有一個缺點螟深,你不能傳遞任何參數(shù)給這個源文件(除非你在腳本文件本身中創(chuàng)建一個有效的變量)。

如果你需要更高級的功能烫葬,你也可以使用 script 子命令界弧,這個命令允許你用自定義的 Python 腳本 管理(add, delete, importlist),通過 script 命令能實現(xiàn)真正的自動化搭综。請閱讀這個優(yōu)秀的教程 Python scripting for LLDB垢箕。為了演示的目的,讓我們創(chuàng)建一個腳本文件 script.py设凹,然后寫一個簡單的命令 print_hello()舰讹,這個命令會在控制臺中打印出“Hello Debugger!“:

import lldb

def print_hello(debugger, command, result, internal_dict):
    print "Hello Debugger!"
    
def __lldb_init_module(debugger, internal_dict):
    debugger.HandleCommand('command script add -f script.print_hello print_hello') // 控制腳本的初始化同時從這個模塊中添加命令
    print 'The "print_hello" python command has been installed and is ready for use.' // 打印確認(rèn)一切正常

接下來我們需要導(dǎo)入一個 Python 模塊,就能開始正常地使用我們的腳本命令了:

(lldb) command import ~/Desktop/script.py

The "print_hello" python command has been installed and is ready for use.

(lldb) print_hello

Hello Debugger!

你可以使用 status 子命令來快速檢查當(dāng)前的環(huán)境信息闪朱,status 會告訴你:SDK 路徑月匣、處理器的架構(gòu)、操作系統(tǒng)版本甚至是該 SDK 可支持的設(shè)備的列表奋姿。

(lldb) platform status

Platform: ios-simulator
Triple: x86_64-apple-macosx
OS Version: 10.12.5 (16F73)
Kernel: Darwin Kernel Version 16.6.0: Fri Apr 14 16:21:16 PDT 2017; root:xnu-3789.60.24~6/RELEASE_X86_64
Hostname: 127.0.0.1
WorkingDir: /
SDK Path: "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk"

Available devices:
614F8701-3D93-4B43-AE86-46A42FEB905A: iPhone 4s
CD516CF7-2AE7-4127-92DF-F536FE56BA22: iPhone 5
0D76F30F-2332-4E0C-9F00-B86F009D59A3: iPhone 5s
3084003F-7626-462A-825B-193E6E5B9AA7: iPhone 6
...

你不能在 Xcode 中使用 LLDB GUI 模式锄开,但你總是可以從終端使用(LLDB GUI 模式)。

(lldb) gui

// 如果你試著在 Xcode 中執(zhí)行這個 gui 命令的話称诗,你將會看到這個錯誤:the gui command requires an interactive terminal萍悴。

這就是 LLDB GUI 模式看起來的樣子。

結(jié)論:

在這篇文章中寓免,我只是淺析了 LLDB 的皮毛知識而已癣诱,即使 LLDB 已經(jīng)有好些年頭了,但是仍然有許多人并沒有完全發(fā)揮出它的潛能袜香。我只是對基本的方法做了一個概述撕予,以及談了 LLDB 如何自動化調(diào)試步驟。我希望這會是有幫助的蜈首。

還有很多 LLDB 的方法并沒有寫到实抡,然后還有一些視圖調(diào)試技術(shù)我沒有提及欠母。如果你對這些話題感興趣的話,請在下面留下你的評論吆寨,我會更加樂于寫這些話題赏淌。

我強(qiáng)烈建議你打開終端,啟動 LLDB啄清,只需要敲入 help六水,就會向你展示完整的文檔。你可以花費(fèi)數(shù)小時去閱讀盒延,但是我保證這將是一個合理的時間投資缩擂。因為了解你的工具是工程師真正產(chǎn)出的唯一途徑鼠冕。


  • LLDB 官方網(wǎng)站 — ?你會在這里找到所有與 LLDB 相關(guān)的材料添寺。文檔、指南懈费、教程计露、源文件以及更多。
  • LLDB Quick Start Guide by Apple — 同樣地憎乙,Apple 提供了很好的文檔票罐。這篇指南能幫你快速上手 LLDB,當(dāng)然泞边,他們也敘述了怎樣不通過 Xcode 地用 LLDB 調(diào)試该押。
  • How debuggers work: Part 1?—?Basics — 我非常喜歡這個系列的文章,這是對調(diào)試器實際工作方式很好的概述阵谚。文章介紹了用 C 語言手工編寫的調(diào)試器代碼要遵循的所有基本原理蚕礼。我強(qiáng)烈建議你去閱讀這個優(yōu)秀系列的所有部分(第2部分, 第3部分)。
  • WWDC14 Advanced Swift Debugging in LLDB — 關(guān)于在 LLDB 中用 Swift 調(diào)試的一篇不錯的概述梢什,也講了 LLDB 如何通過內(nèi)建的方法和特性實現(xiàn)完整的調(diào)試操作奠蹬,來幫你變得更加高效。
  • Introduction To LLDB Python Scripting — 這篇介紹 LLDB Python 腳本的指南能讓你快速上手嗡午。
  • Dancing in the Debugger. A Waltz with LLDB? — 對 LLDB 一些基礎(chǔ)知識的介紹囤躁,有些知識有點過時了(比如說 (lldb) thread return 命令)。遺憾的是荔睹,它不能直接用于 Swift狸演,因為它會對引用計數(shù)帶了一些潛在的隱患。但是僻他,這仍然是你開始 LLDB 之旅不錯的文章宵距。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市中姜,隨后出現(xiàn)的幾起案子消玄,更是在濱河造成了極大的恐慌跟伏,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件翩瓜,死亡現(xiàn)場離奇詭異受扳,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)兔跌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門勘高,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人坟桅,你說我怎么就攤上這事华望。” “怎么了仅乓?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵赖舟,是天一觀的道長。 經(jīng)常有香客問我夸楣,道長宾抓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任豫喧,我火速辦了婚禮石洗,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘紧显。我一直安慰自己讲衫,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布孵班。 她就那樣靜靜地躺著涉兽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪重父。 梳的紋絲不亂的頭發(fā)上花椭,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天,我揣著相機(jī)與錄音房午,去河邊找鬼矿辽。 笑死,一個胖子當(dāng)著我的面吹牛郭厌,可吹牛的內(nèi)容都是我干的袋倔。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼折柠,長吁一口氣:“原來是場噩夢啊……” “哼宾娜!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起扇售,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤前塔,失蹤者是張志新(化名)和其女友劉穎嚣艇,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體华弓,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡食零,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了寂屏。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贰谣。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖迁霎,靈堂內(nèi)的尸體忽然破棺而出吱抚,到底是詐尸還是另有隱情,我是刑警寧澤考廉,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布秘豹,位于F島的核電站,受9級特大地震影響芝此,放射性物質(zhì)發(fā)生泄漏憋肖。R本人自食惡果不足惜因痛,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一婚苹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鸵膏,春花似錦膊升、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至债查,卻和暖如春非区,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背盹廷。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工征绸, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人俄占。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓管怠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親缸榄。 傳聞我的和親對象是個殘疾皇子渤弛,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內(nèi)容