github.com/Rookout/GoSDK@v0.1.48/pkg/services/instrumentation/dwarf/line/state_machine.go (about)

     1  // The MIT License (MIT)
     2  
     3  // Copyright (c) 2014 Derek Parker
     4  
     5  // Permission is hereby granted, free of charge, to any person obtaining a copy of
     6  // this software and associated documentation files (the "Software"), to deal in
     7  // the Software without restriction, including without limitation the rights to
     8  // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
     9  // the Software, and to permit persons to whom the Software is furnished to do so,
    10  // subject to the following conditions:
    11  
    12  // The above copyright notice and this permission notice shall be included in all
    13  // copies or substantial portions of the Software.
    14  
    15  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    16  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
    17  // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
    18  // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
    19  // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
    20  // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    21  
    22  package line
    23  
    24  import (
    25  	"bytes"
    26  	"encoding/binary"
    27  	"errors"
    28  	"fmt"
    29  	"io"
    30  
    31  	"github.com/Rookout/GoSDK/pkg/services/instrumentation/dwarf/util"
    32  )
    33  
    34  type Location struct {
    35  	File    string
    36  	Line    int
    37  	Address uint64
    38  	Delta   int
    39  }
    40  
    41  type StateMachine struct {
    42  	dbl           *DebugLineInfo
    43  	file          string
    44  	line          int
    45  	address       uint64
    46  	column        uint
    47  	isStmt        bool
    48  	isa           uint64 
    49  	basicBlock    bool
    50  	endSeq        bool
    51  	lastDelta     int
    52  	prologueEnd   bool
    53  	epilogueBegin bool
    54  	
    55  	
    56  	
    57  	
    58  	valid bool
    59  
    60  	started bool
    61  
    62  	buf     *bytes.Buffer 
    63  	opcodes []opcodefn
    64  
    65  	definedFiles []*FileEntry 
    66  
    67  	lastAddress uint64
    68  	lastFile    string
    69  	lastLine    int
    70  	ptrSize     int
    71  }
    72  
    73  type opcodefn func(*StateMachine, *bytes.Buffer)
    74  
    75  
    76  const (
    77  	DW_LNS_copy             = 1
    78  	DW_LNS_advance_pc       = 2
    79  	DW_LNS_advance_line     = 3
    80  	DW_LNS_set_file         = 4
    81  	DW_LNS_set_column       = 5
    82  	DW_LNS_negate_stmt      = 6
    83  	DW_LNS_set_basic_block  = 7
    84  	DW_LNS_const_add_pc     = 8
    85  	DW_LNS_fixed_advance_pc = 9
    86  	DW_LNS_prologue_end     = 10
    87  	DW_LNS_epilogue_begin   = 11
    88  	DW_LNS_set_isa          = 12
    89  )
    90  
    91  
    92  const (
    93  	DW_LINE_end_sequence      = 1
    94  	DW_LINE_set_address       = 2
    95  	DW_LINE_define_file       = 3
    96  	DW_LINE_set_discriminator = 4
    97  )
    98  
    99  var standardopcodes = map[byte]opcodefn{
   100  	DW_LNS_copy:             copyfn,
   101  	DW_LNS_advance_pc:       advancepc,
   102  	DW_LNS_advance_line:     advanceline,
   103  	DW_LNS_set_file:         setfile,
   104  	DW_LNS_set_column:       setcolumn,
   105  	DW_LNS_negate_stmt:      negatestmt,
   106  	DW_LNS_set_basic_block:  setbasicblock,
   107  	DW_LNS_const_add_pc:     constaddpc,
   108  	DW_LNS_fixed_advance_pc: fixedadvancepc,
   109  	DW_LNS_prologue_end:     prologueend,
   110  	DW_LNS_epilogue_begin:   epiloguebegin,
   111  	DW_LNS_set_isa:          setisa,
   112  }
   113  
   114  var extendedopcodes = map[byte]opcodefn{
   115  	DW_LINE_end_sequence:      endsequence,
   116  	DW_LINE_set_address:       setaddress,
   117  	DW_LINE_define_file:       definefile,
   118  	DW_LINE_set_discriminator: setdiscriminator,
   119  }
   120  
   121  func newStateMachine(dbl *DebugLineInfo, instructions []byte, ptrSize int) *StateMachine {
   122  	opcodes := make([]opcodefn, len(standardopcodes)+1)
   123  	opcodes[0] = execExtendedOpcode
   124  	for op := range standardopcodes {
   125  		opcodes[op] = standardopcodes[op]
   126  	}
   127  	var file string
   128  	if len(dbl.FileNames) > 0 {
   129  		file = dbl.FileNames[0].Path
   130  	}
   131  	sm := &StateMachine{
   132  		dbl:         dbl,
   133  		file:        file,
   134  		line:        1,
   135  		buf:         bytes.NewBuffer(instructions),
   136  		opcodes:     opcodes,
   137  		isStmt:      dbl.Prologue.InitialIsStmt == uint8(1),
   138  		address:     dbl.staticBase,
   139  		lastAddress: ^uint64(0),
   140  		ptrSize:     ptrSize,
   141  	}
   142  	return sm
   143  }
   144  
   145  
   146  
   147  func (lineInfo *DebugLineInfo) AllPCsForFileLines(f string, m map[int][]uint64) {
   148  	if lineInfo == nil {
   149  		return
   150  	}
   151  
   152  	var (
   153  		lastAddr uint64
   154  		sm       = newStateMachine(lineInfo, lineInfo.Instructions, lineInfo.ptrSize)
   155  	)
   156  
   157  	for {
   158  		if err := sm.next(); err != nil {
   159  			if lineInfo.Logf != nil {
   160  				lineInfo.Logf("AllPCsForFileLine error: %v", err)
   161  			}
   162  			break
   163  		}
   164  		if sm.address != lastAddr && sm.isStmt && sm.valid && sm.file == f {
   165  			if pcs, ok := m[sm.line]; ok {
   166  				pcs = append(pcs, sm.address)
   167  				m[sm.line] = pcs
   168  				lastAddr = sm.address
   169  			}
   170  		}
   171  	}
   172  }
   173  
   174  var ErrNoSource = errors.New("no source available")
   175  
   176  
   177  
   178  func (lineInfo *DebugLineInfo) AllPCsBetween(begin, end uint64, excludeFile string, excludeLine int) ([]uint64, error) {
   179  	if lineInfo == nil {
   180  		return nil, ErrNoSource
   181  	}
   182  
   183  	var (
   184  		pcs      []uint64
   185  		lastaddr uint64
   186  		sm       = newStateMachine(lineInfo, lineInfo.Instructions, lineInfo.ptrSize)
   187  	)
   188  
   189  	for {
   190  		if err := sm.next(); err != nil {
   191  			if lineInfo.Logf != nil {
   192  				lineInfo.Logf("AllPCsBetween error: %v", err)
   193  			}
   194  			break
   195  		}
   196  		if !sm.valid {
   197  			continue
   198  		}
   199  		if (sm.address > end) && (end >= sm.lastAddress) {
   200  			break
   201  		}
   202  		if sm.address >= begin && sm.address <= end && sm.address > lastaddr && sm.isStmt && !sm.endSeq && ((sm.file != excludeFile) || (sm.line != excludeLine)) {
   203  			lastaddr = sm.address
   204  			pcs = append(pcs, sm.address)
   205  		}
   206  	}
   207  	return pcs, nil
   208  }
   209  
   210  
   211  
   212  func (sm *StateMachine) copy() *StateMachine {
   213  	r := *sm
   214  	r.buf = bytes.NewBuffer(sm.buf.Bytes())
   215  	return &r
   216  }
   217  
   218  func (lineInfo *DebugLineInfo) stateMachineForEntry(basePC uint64) (sm *StateMachine) {
   219  	lineInfo.stateMachineCacheLock.RLock()
   220  	sm = lineInfo.stateMachineCache[basePC]
   221  	lineInfo.stateMachineCacheLock.RUnlock()
   222  
   223  	if sm == nil {
   224  		sm = newStateMachine(lineInfo, lineInfo.Instructions, lineInfo.ptrSize)
   225  		sm.PCToLine(basePC)
   226  		lineInfo.stateMachineCacheLock.Lock()
   227  		lineInfo.stateMachineCache[basePC] = sm
   228  		lineInfo.stateMachineCacheLock.Unlock()
   229  	}
   230  	sm = sm.copy()
   231  	return
   232  }
   233  
   234  
   235  
   236  
   237  
   238  
   239  func (lineInfo *DebugLineInfo) PCToLine(basePC, pc uint64) (string, int) {
   240  	if lineInfo == nil {
   241  		return "", 0
   242  	}
   243  	if basePC > pc {
   244  		panic(fmt.Errorf("basePC after pc %#x %#x", basePC, pc))
   245  	}
   246  
   247  	sm := lineInfo.stateMachineFor(basePC, pc)
   248  
   249  	file, line, _ := sm.PCToLine(pc)
   250  	return file, line
   251  }
   252  
   253  func (lineInfo *DebugLineInfo) stateMachineFor(basePC, pc uint64) *StateMachine {
   254  	var sm *StateMachine
   255  	if basePC == 0 {
   256  		sm = newStateMachine(lineInfo, lineInfo.Instructions, lineInfo.ptrSize)
   257  	} else {
   258  		
   259  		
   260  		
   261  		
   262  		lineInfo.lastMachineCacheLock.RLock()
   263  		sm = lineInfo.lastMachineCache[basePC]
   264  		lineInfo.lastMachineCacheLock.RUnlock()
   265  		if sm == nil || sm.lastAddress >= pc {
   266  			sm = lineInfo.stateMachineForEntry(basePC)
   267  			lineInfo.lastMachineCacheLock.Lock()
   268  			lineInfo.lastMachineCache[basePC] = sm
   269  			lineInfo.lastMachineCacheLock.Unlock()
   270  		}
   271  	}
   272  	return sm
   273  }
   274  
   275  func (sm *StateMachine) PCToLine(pc uint64) (string, int, bool) {
   276  	if !sm.started {
   277  		if err := sm.next(); err != nil {
   278  			if sm.dbl.Logf != nil {
   279  				sm.dbl.Logf("PCToLine error: %v", err)
   280  			}
   281  			return "", 0, false
   282  		}
   283  	}
   284  	if sm.lastAddress > pc && sm.lastAddress != ^uint64(0) {
   285  		return "", 0, false
   286  	}
   287  	for {
   288  		if sm.valid {
   289  			if (sm.address > pc) && (pc >= sm.lastAddress) {
   290  				return sm.lastFile, sm.lastLine, true
   291  			}
   292  			if sm.address == pc {
   293  				return sm.file, sm.line, true
   294  			}
   295  		}
   296  		if err := sm.next(); err != nil {
   297  			if sm.dbl.Logf != nil {
   298  				sm.dbl.Logf("PCToLine error: %v", err)
   299  			}
   300  			break
   301  		}
   302  	}
   303  	if sm.valid {
   304  		return sm.file, sm.line, true
   305  	}
   306  	return "", 0, false
   307  }
   308  
   309  
   310  type PCStmt struct {
   311  	PC   uint64
   312  	Stmt bool
   313  }
   314  
   315  
   316  func (lineInfo *DebugLineInfo) LineToPCs(filename string, lineno int) []PCStmt {
   317  	if lineInfo == nil {
   318  		return nil
   319  	}
   320  
   321  	sm := newStateMachine(lineInfo, lineInfo.Instructions, lineInfo.ptrSize)
   322  
   323  	pcstmts := []PCStmt{}
   324  
   325  	for {
   326  		if err := sm.next(); err != nil {
   327  			if lineInfo.Logf != nil && err != io.EOF {
   328  				lineInfo.Logf("LineToPCs error: %v", err)
   329  			}
   330  			break
   331  		}
   332  		if sm.line == lineno && sm.file == filename && sm.valid {
   333  			pcstmts = append(pcstmts, PCStmt{sm.address, sm.isStmt})
   334  		}
   335  	}
   336  
   337  	return pcstmts
   338  }
   339  
   340  
   341  func (lineInfo *DebugLineInfo) PrologueEndPC(start, end uint64) (pc uint64, file string, line int, ok bool) {
   342  	if lineInfo == nil {
   343  		return 0, "", 0, false
   344  	}
   345  
   346  	sm := lineInfo.stateMachineForEntry(start)
   347  	for {
   348  		if sm.valid {
   349  			if sm.address >= end {
   350  				return 0, "", 0, false
   351  			}
   352  			if sm.prologueEnd {
   353  				return sm.address, sm.file, sm.line, true
   354  			}
   355  		}
   356  		if err := sm.next(); err != nil {
   357  			if lineInfo.Logf != nil {
   358  				lineInfo.Logf("PrologueEnd error: %v", err)
   359  			}
   360  			return 0, "", 0, false
   361  		}
   362  	}
   363  }
   364  
   365  
   366  
   367  func (lineInfo *DebugLineInfo) FirstStmtForLine(start, end uint64) (pc uint64, file string, line int, ok bool) {
   368  	first := true
   369  	sm := lineInfo.stateMachineForEntry(start)
   370  	for {
   371  		if sm.valid {
   372  			if sm.address >= end {
   373  				return 0, "", 0, false
   374  			}
   375  			if first {
   376  				first = false
   377  				file, line = sm.file, sm.line
   378  			}
   379  			if sm.isStmt && sm.file == file && sm.line == line {
   380  				return sm.address, sm.file, sm.line, true
   381  			}
   382  		}
   383  		if err := sm.next(); err != nil {
   384  			if lineInfo.Logf != nil {
   385  				lineInfo.Logf("FirstStmtForLine error: %v", err)
   386  			}
   387  			return 0, "", 0, false
   388  		}
   389  	}
   390  }
   391  
   392  func (lineInfo *DebugLineInfo) FirstFile() string {
   393  	sm := newStateMachine(lineInfo, lineInfo.Instructions, lineInfo.ptrSize)
   394  	for {
   395  		if sm.valid {
   396  			return sm.file
   397  		}
   398  		if err := sm.next(); err != nil {
   399  			if lineInfo.Logf != nil {
   400  				lineInfo.Logf("FirstFile error: %v", err)
   401  			}
   402  			return ""
   403  		}
   404  	}
   405  }
   406  
   407  func (sm *StateMachine) next() error {
   408  	sm.started = true
   409  	if sm.valid {
   410  		sm.lastAddress, sm.lastFile, sm.lastLine = sm.address, sm.file, sm.line
   411  
   412  		
   413  		
   414  		sm.basicBlock = false
   415  		sm.prologueEnd = false
   416  		sm.epilogueBegin = false
   417  	}
   418  	if sm.endSeq {
   419  		sm.endSeq = false
   420  		sm.file = sm.dbl.FileNames[0].Path
   421  		sm.line = 1
   422  		sm.column = 0
   423  		sm.isa = 0
   424  		sm.isStmt = sm.dbl.Prologue.InitialIsStmt == uint8(1)
   425  		sm.basicBlock = false
   426  		sm.lastAddress = ^uint64(0)
   427  	}
   428  	b, err := sm.buf.ReadByte()
   429  	if err != nil {
   430  		return err
   431  	}
   432  	if b < sm.dbl.Prologue.OpcodeBase {
   433  		if int(b) < len(sm.opcodes) {
   434  			sm.valid = false
   435  			sm.opcodes[b](sm, sm.buf)
   436  		} else {
   437  			
   438  			
   439  			opnum := sm.dbl.Prologue.StdOpLengths[b-1]
   440  			for i := 0; i < int(opnum); i++ {
   441  				util.DecodeSLEB128(sm.buf)
   442  			}
   443  			fmt.Printf("unknown opcode %d(0x%x), %d arguments, file %s, line %d, address 0x%x\n", b, b, opnum, sm.file, sm.line, sm.address)
   444  		}
   445  	} else {
   446  		execSpecialOpcode(sm, b)
   447  	}
   448  	return nil
   449  }
   450  
   451  func execSpecialOpcode(sm *StateMachine, instr byte) {
   452  	var (
   453  		opcode  = uint8(instr)
   454  		decoded = opcode - sm.dbl.Prologue.OpcodeBase
   455  	)
   456  
   457  	sm.lastDelta = int(sm.dbl.Prologue.LineBase + int8(decoded%sm.dbl.Prologue.LineRange))
   458  	sm.line += sm.lastDelta
   459  	sm.address += uint64(decoded/sm.dbl.Prologue.LineRange) * uint64(sm.dbl.Prologue.MinInstrLength)
   460  	sm.valid = true
   461  }
   462  
   463  func execExtendedOpcode(sm *StateMachine, buf *bytes.Buffer) {
   464  	_, _ = util.DecodeULEB128(buf)
   465  	b, _ := buf.ReadByte()
   466  	if fn, ok := extendedopcodes[b]; ok {
   467  		fn(sm, buf)
   468  	}
   469  }
   470  
   471  func copyfn(sm *StateMachine, buf *bytes.Buffer) {
   472  	sm.valid = true
   473  }
   474  
   475  func advancepc(sm *StateMachine, buf *bytes.Buffer) {
   476  	addr, _ := util.DecodeULEB128(buf)
   477  	sm.address += addr * uint64(sm.dbl.Prologue.MinInstrLength)
   478  }
   479  
   480  func advanceline(sm *StateMachine, buf *bytes.Buffer) {
   481  	line, _ := util.DecodeSLEB128(buf)
   482  	sm.line += int(line)
   483  	sm.lastDelta = int(line)
   484  }
   485  
   486  func setfile(sm *StateMachine, buf *bytes.Buffer) {
   487  	i, _ := util.DecodeULEB128(buf)
   488  	if sm.dbl.Prologue.Version < 5 {
   489  		
   490  		i--
   491  	}
   492  	if i < uint64(len(sm.dbl.FileNames)) {
   493  		sm.file = sm.dbl.FileNames[i].Path
   494  	} else {
   495  		j := i - uint64(len(sm.dbl.FileNames))
   496  		if j < uint64(len(sm.definedFiles)) {
   497  			sm.file = sm.definedFiles[j].Path
   498  		} else {
   499  			sm.file = ""
   500  		}
   501  	}
   502  }
   503  
   504  func setcolumn(sm *StateMachine, buf *bytes.Buffer) {
   505  	c, _ := util.DecodeULEB128(buf)
   506  	sm.column = uint(c)
   507  }
   508  
   509  func negatestmt(sm *StateMachine, buf *bytes.Buffer) {
   510  	sm.isStmt = !sm.isStmt
   511  }
   512  
   513  func setbasicblock(sm *StateMachine, buf *bytes.Buffer) {
   514  	sm.basicBlock = true
   515  }
   516  
   517  func constaddpc(sm *StateMachine, buf *bytes.Buffer) {
   518  	sm.address += uint64((255-sm.dbl.Prologue.OpcodeBase)/sm.dbl.Prologue.LineRange) * uint64(sm.dbl.Prologue.MinInstrLength)
   519  }
   520  
   521  func fixedadvancepc(sm *StateMachine, buf *bytes.Buffer) {
   522  	var operand uint16
   523  	binary.Read(buf, binary.LittleEndian, &operand)
   524  
   525  	sm.address += uint64(operand)
   526  }
   527  
   528  func endsequence(sm *StateMachine, buf *bytes.Buffer) {
   529  	sm.endSeq = true
   530  	sm.valid = sm.dbl.endSeqIsValid
   531  }
   532  
   533  func setaddress(sm *StateMachine, buf *bytes.Buffer) {
   534  	addr, err := util.ReadUintRaw(buf, binary.LittleEndian, sm.ptrSize)
   535  	if err != nil {
   536  		panic(err)
   537  	}
   538  	sm.address = addr + sm.dbl.staticBase
   539  }
   540  
   541  func setdiscriminator(sm *StateMachine, buf *bytes.Buffer) {
   542  	_, _ = util.DecodeULEB128(buf)
   543  }
   544  
   545  func definefile(sm *StateMachine, buf *bytes.Buffer) {
   546  	if entry := readFileEntry(sm.dbl, sm.buf, false); entry != nil {
   547  		sm.definedFiles = append(sm.definedFiles, entry)
   548  	}
   549  }
   550  
   551  func prologueend(sm *StateMachine, buf *bytes.Buffer) {
   552  	sm.prologueEnd = true
   553  }
   554  
   555  func epiloguebegin(sm *StateMachine, buf *bytes.Buffer) {
   556  	sm.epilogueBegin = true
   557  }
   558  
   559  func setisa(sm *StateMachine, buf *bytes.Buffer) {
   560  	c, _ := util.DecodeULEB128(buf)
   561  	sm.isa = c
   562  }