Netbeans Support for Tcl

Check-in [ba318ba05c]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Breakpoints are working. User interface and debugServer.tcl work as expected.
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: ba318ba05cad467e0f57a312aa0305edbdbb29e7
User & Date: dmp 2011-08-05 14:35:14
Context
2011-08-07
19:53
Fixed issue with new version of debugServer.tcl in Netbeans User Directory. Output from debugServer.tcl changed to stderr. check-in: e47870099f user: dmp tags: trunk
2011-08-05
14:35
Breakpoints are working. User interface and debugServer.tcl work as expected. check-in: ba318ba05c user: dmp tags: trunk
09:59
Added support for breakpoints in debugServer.tcl. check-in: 0db6cfc1ab user: dmp tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to TclPlugin/src/org/netbeans/modules/languages/tcl/debugger/TclDebuggerJavaPart.java.

1
2
3
4
5
6
7
8
9
10
11
12


13

14
15
16
17
18
19
20
/*
 * Created during Google Summer Of Code 2011.
 * For Tcl/Tk Community by student Michał Poczwardowski
 * License: BSD
 */
package org.netbeans.modules.languages.tcl.debugger;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;


import java.util.NoSuchElementException;

import org.netbeans.api.debugger.DebuggerManager;
import org.netbeans.api.debugger.Session;
import org.netbeans.modules.languages.tcl.project.TclProject;
import org.netbeans.modules.languages.tcl.run.RunTclsh;
import org.netbeans.spi.viewmodel.ModelListener;
import org.openide.util.Exceptions;













>
>

>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
 * Created during Google Summer Of Code 2011.
 * For Tcl/Tk Community by student Michał Poczwardowski
 * License: BSD
 */
package org.netbeans.modules.languages.tcl.debugger;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import org.netbeans.api.debugger.Breakpoint;
import org.netbeans.api.debugger.DebuggerManager;
import org.netbeans.api.debugger.Session;
import org.netbeans.modules.languages.tcl.project.TclProject;
import org.netbeans.modules.languages.tcl.run.RunTclsh;
import org.netbeans.spi.viewmodel.ModelListener;
import org.openide.util.Exceptions;

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
	private int tclPartPortNumber=31337;
	private String variablesStringLocals="";
	private String variablesStringGlobals="";
	private ModelListener modelListener=null;

	public void addModelListener( ModelListener ml ) {
		//System.out.println("addModelListner inside TclDebuggerJavaPart : "+ml);
		this.modelListener = ml;
	}


	public TclDebuggerJavaPart( TclDebuggerEngineProvider engineProvider ) {

		bindProject();

		this.engineProvider=engineProvider;








|

<







38
39
40
41
42
43
44
45
46

47
48
49
50
51
52
53
	private int tclPartPortNumber=31337;
	private String variablesStringLocals="";
	private String variablesStringGlobals="";
	private ModelListener modelListener=null;

	public void addModelListener( ModelListener ml ) {
		//System.out.println("addModelListner inside TclDebuggerJavaPart : "+ml);
		this.modelListener=ml;
	}


	public TclDebuggerJavaPart( TclDebuggerEngineProvider engineProvider ) {

		bindProject();

		this.engineProvider=engineProvider;

101
102
103
104
105
106
107
108
109
110
111



112
113
114
115

116












































117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
			}

		}
	}

	private void runTclPart() {

		String fileToRunPath = project.getFileToRun().getAbsolutePath();
		new RunTclsh( tclPartFileName, this.tclPartFile.getAbsolutePath(), fileToRunPath, TclProject.getDirectoryFromPath( fileToRunPath), this.tclPartPortNumber + "" );
	}




	private void createConnectionToTclPart() {

		if( tclPart == null ) {
			tclPart=new ClientToTclPart( this.tclPartPortNumber );

			getExecutionStatus();












































		}
	}

	private void getExecutionStatus() {

		tclPart.printlnToSocket( "status" );
		try {
			// get first line → file localization
			String fileName=tclPart.nextLineFromFromSocket();
			currentFile=new File( fileName );

			// get second line → line in file
			currentLineNumber=Integer.parseInt( tclPart.nextLineFromFromSocket() );
			programCounter.modify();

			// get third line → number of variablesStringLocals
			int numberOfVariables=Integer.parseInt( tclPart.nextLineFromFromSocket() );
			variablesStringLocals = "";

			// every following numberOfVariables line contains name nad value of variable
			for( int i=1; i <= numberOfVariables; i++ ) {
				variablesStringLocals += tclPart.nextLineFromFromSocket()+"\n";
			}

			// get next line → number of variablesStringGlobals
			numberOfVariables=Integer.parseInt( tclPart.nextLineFromFromSocket() );
			variablesStringGlobals = "";
			// every following numberOfVariables line contains name nad value of variable
			for( int i=1; i <= numberOfVariables; i++ ) {
				variablesStringGlobals += tclPart.nextLineFromFromSocket()+"\n";
			}
			
			updateTclVariablesModel();
			
		} catch( NoSuchElementException e ) {
			System.out.println( ">>> End of debugging…" );
			actionKill();
		}
		
	}

	// Action handlers below:
	//
	public void actionStepOver() {
		createConnectionToTclPart();
		tclPart.printlnToSocket( "stepover" );







|
|


>
>
>
|



>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

















|



|




|


|

|

|




|







103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
			}

		}
	}

	private void runTclPart() {

		String fileToRunPath=project.getFileToRun().getAbsolutePath();
		new RunTclsh( tclPartFileName, this.tclPartFile.getAbsolutePath(), fileToRunPath, TclProject.getDirectoryFromPath( fileToRunPath ), this.tclPartPortNumber + "" );
	}

	/*
	 * Returns true if connection is created
	 */
	private boolean createConnectionToTclPart() {

		if( tclPart == null ) {
			tclPart=new ClientToTclPart( this.tclPartPortNumber );
			sendBreakpoints();
			getExecutionStatus();
			return true;
		}
		return false;
	}

	private void sendBreakpoints() {


		Breakpoint[] breakpoints=DebuggerManager.getDebuggerManager().getBreakpoints();
		int iter;
		int breakpointsCount=breakpoints.length;

		if( breakpointsCount != 0 ) {

			tclPart.printlnToSocket( "breakpoints" );

			// foreach breakpoint
			String filePath, lineNumber, lines;
			HashMap<String, String> bpoints=new HashMap<String, String>();
			for( iter=0; iter < breakpointsCount; iter++ ) {
				if( breakpoints[iter] instanceof TclBreakpoint && ( ( ( TclBreakpoint )breakpoints[iter] ).getLine() != null ) ) {
					TclBreakpoint bp=( TclBreakpoint )breakpoints[iter];

					filePath=bp.getFile().getPath();
					lineNumber=Integer.toString( bp.getLine().getLineNumber() + 1 );

					if( bpoints.containsKey( filePath ) ) {
						lines=bpoints.get( filePath );
						lines+=" " + lineNumber;
						bpoints.put( filePath, lines );
					} else {
						bpoints.put( filePath, lineNumber );
					}
				}
			}

			// send to server:
			// // number of files
			tclPart.printlnToSocket( Integer.toString( bpoints.size() ) );
			// // foreach file line numbers
			for( Map.Entry<String, String> point:bpoints.entrySet() ) {
				tclPart.printlnToSocket( point.getKey() );
				tclPart.printlnToSocket( point.getValue() );
			}
		}
	}

	private void getExecutionStatus() {

		tclPart.printlnToSocket( "status" );
		try {
			// get first line → file localization
			String fileName=tclPart.nextLineFromFromSocket();
			currentFile=new File( fileName );

			// get second line → line in file
			currentLineNumber=Integer.parseInt( tclPart.nextLineFromFromSocket() );
			programCounter.modify();

			// get third line → number of variablesStringLocals
			int numberOfVariables=Integer.parseInt( tclPart.nextLineFromFromSocket() );
			variablesStringLocals="";

			// every following numberOfVariables line contains name nad value of variable
			for( int i=1; i <= numberOfVariables; i++ ) {
				variablesStringLocals+=tclPart.nextLineFromFromSocket() + "\n";
			}

			// get next line → number of variablesStringGlobals
			numberOfVariables=Integer.parseInt( tclPart.nextLineFromFromSocket() );
			variablesStringGlobals="";
			// every following numberOfVariables line contains name nad value of variable
			for( int i=1; i <= numberOfVariables; i++ ) {
				variablesStringGlobals+=tclPart.nextLineFromFromSocket() + "\n";
			}

			updateTclVariablesModel();

		} catch( NoSuchElementException e ) {
			System.out.println( ">>> End of debugging…" );
			actionKill();
		}

	}

	// Action handlers below:
	//
	public void actionStepOver() {
		createConnectionToTclPart();
		tclPart.printlnToSocket( "stepover" );
170
171
172
173
174
175
176
177





178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
	}

	void actionPause() {
		createConnectionToTclPart();
	}

	void actionContinue() {
		createConnectionToTclPart();





	}

	void actionRunToCursor() {
		createConnectionToTclPart();
	}

	void actionKill() {
		createConnectionToTclPart();
		tclPart.printlnToSocket( "exit" );
		tclPart.close();
		programCounter.detach();
		engineProvider.getDestructor().killEngine();
	}

	private void updateTclVariablesModel() {
		if( modelListener != null) {
			modelListener.modelChanged( null);
		}
	}

	// Getters below:
	//
	public File getCurrentFile() {
		return currentFile;
	}

	public int getCurrentLineNumber() {
		return currentLineNumber;
	}

	public String getVariablesStringLocals() {
		return variablesStringLocals;
	}
	
	public String getVariablesStringGlobals() {
		return variablesStringGlobals;
	}

}







|
>
>
>
>
>















|
|
















|





220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
	}

	void actionPause() {
		createConnectionToTclPart();
	}

	void actionContinue() {
		if( !createConnectionToTclPart() ) { // for next runs (not first one)
			sendBreakpoints();
			tclPart.printlnToSocket( "continue" );
			getExecutionStatus();
		}

	}

	void actionRunToCursor() {
		createConnectionToTclPart();
	}

	void actionKill() {
		createConnectionToTclPart();
		tclPart.printlnToSocket( "exit" );
		tclPart.close();
		programCounter.detach();
		engineProvider.getDestructor().killEngine();
	}

	private void updateTclVariablesModel() {
		if( modelListener != null ) {
			modelListener.modelChanged( null );
		}
	}

	// Getters below:
	//
	public File getCurrentFile() {
		return currentFile;
	}

	public int getCurrentLineNumber() {
		return currentLineNumber;
	}

	public String getVariablesStringLocals() {
		return variablesStringLocals;
	}

	public String getVariablesStringGlobals() {
		return variablesStringGlobals;
	}

}

Changes to TclPlugin/src/org/netbeans/modules/languages/tcl/debugger/resources/debugServer.tcl.

15
16
17
18
19
20
21

22
23
24
25
26
27
28
  set breakpointsStatus 0
  variable breakpoint

# execution args:
  variable portNumber
  variable tclScript
  variable currentDirectory


# setData variables
  variable fileName
  variable lineNumber
  variable infoFrame 

# other globals







>







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  set breakpointsStatus 0
  variable breakpoint

# execution args:
  variable portNumber
  variable tclScript
  variable currentDirectory
  variable isVerbose

# setData variables
  variable fileName
  variable lineNumber
  variable infoFrame 

# other globals
75
76
77
78
79
80
81


82
83
84
85


86
87
88
89
90
91


92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121

































122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167

proc setData args {
	set debug::infoFrame [info frame]
	set infoDict [info frame [expr $debug::infoFrame - $debug::startLevel]]

	if { [dict exists $infoDict line] } {
		set debug::lineNumber [dict get $infoDict line]


	}

	if { [dict exists $infoDict file] } {
		set debug::fileName [dict get $infoDict file]


	}
}

proc sendData args {
 	puts $debug::socketFd $debug::fileName
	puts $debug::socketFd $debug::lineNumber



	set flist [listVars locals [expr [info level] - 1]]
	puts -nonewline $debug::socketFd $flist

	set flist [listVars globals #0]
	puts -nonewline $debug::socketFd $flist

	flush $debug::socketFd
}

proc serverProc {channel clientaddr clientport} {
	set debug::socketFd $channel
	puts stderr ">>> connection from $clientaddr:$clientport"

	set debug::serverProcStarted 1
	set debug::doNotWait 1
}

proc step {cmdStr op} {

	if { $debug::doNotWait == 0 } {
		puts stderr ">>> Waiting for connection..."
		set debug::startLevel [expr [info level]]
		vwait debug::serverProcStarted
		# return beacuse first step is located inside debugServer.tcl = ommited
		return 
	}

	setData
   	puts stderr ">>> \[ $cmdStr \]"


































	while { true } {
	
		if { $debug::breakpointsStatus != 0 } {
			set breakpointReached 0
			
			# Step everything, till next breakpoint
			if { [info exists debug::breakpoints($debug::fileName)] } {
				#puts "breakpoint In THIS LAP"
				set lines $debug::breakpoints($debug::fileName)
				foreach line $lines {
					if { $line == $debug::lineNumber } {
						set breakpointReached 1
						puts " >> reached breakpoint file $debug::fileName line $line" 
					}
				}
			}

			if { $breakpointReached == 0 } {
				#puts "BOX NOT this lap"
				return
			}
		} 

		# Perform single stepping…
		
		if { $debug::stepOverLevel != -1 } {
			# Now step everything, till next equal or lower stepOverLevel
			if { $debug::stepOverLevel < $debug::infoFrame } {
				break
			} else {
				# Make stepover inactive
				set debug::stepOverLevel -1
			}

		}	
			
		set operation [gets $debug::socketFd]
   		puts stderr ">>> operation: $operation"

		switch $operation {
		   status {
			sendData
		   }
		   step {
			# Activate single stepping mode.







>
>




>
>






>
>












|








|







|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

|







76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164


165































166
167
168
169
170
171
172
173
174

proc setData args {
	set debug::infoFrame [info frame]
	set infoDict [info frame [expr $debug::infoFrame - $debug::startLevel]]

	if { [dict exists $infoDict line] } {
		set debug::lineNumber [dict get $infoDict line]
	} else {
		puts "NOLINE"
	}

	if { [dict exists $infoDict file] } {
		set debug::fileName [dict get $infoDict file]
	} else {
		puts "NOFILE"
	}
}

proc sendData args {
 	puts $debug::socketFd $debug::fileName
	puts $debug::socketFd $debug::lineNumber

 	putsv ">>> sent ($debug::fileName:$debug::lineNumber)"

	set flist [listVars locals [expr [info level] - 1]]
	puts -nonewline $debug::socketFd $flist

	set flist [listVars globals #0]
	puts -nonewline $debug::socketFd $flist

	flush $debug::socketFd
}

proc serverProc {channel clientaddr clientport} {
	set debug::socketFd $channel
	puts ">>> connection from $clientaddr:$clientport"

	set debug::serverProcStarted 1
	set debug::doNotWait 1
}

proc step {cmdStr op} {

	if { $debug::doNotWait == 0 } {
		puts ">>> debugServer.tcl is waiting for connection..."
		set debug::startLevel [expr [info level]]
		vwait debug::serverProcStarted
		# return beacuse first step is located inside debugServer.tcl = ommited
		return 
	}

	setData
   	putsv ">>> \[ $cmdStr \]"


	if { $debug::breakpointsStatus != 0 } {
		set breakpointReached 0
		
		# Step everything, till next breakpoint
		if { [info exists debug::breakpoints($debug::fileName)] } {
			#puts "breakpoint In THIS LAP"
			set lines $debug::breakpoints($debug::fileName)
			foreach line $lines {
				if { $line == $debug::lineNumber } {
					set breakpointReached 1
					putsv ">>> reached breakpoint in file $debug::fileName line $line" 
				}
			}
		}
		if { $breakpointReached == 0 } {
			#puts "BOX NOT this lap"
			return
		}
	} 


	# Perform single stepping…
	if { $debug::stepOverLevel != -1 } {
		# Now step everything, till next equal or lower stepOverLevel
		if { $debug::stepOverLevel < $debug::infoFrame } {
			return
		} else {
			# Make stepover inactive
			set debug::stepOverLevel -1
		}
	}	

	while { true } {
	


		































		set operation [gets $debug::socketFd]
   		putsv ">>> operation: $operation"

		switch $operation {
		   status {
			sendData
		   }
		   step {
			# Activate single stepping mode.
175
176
177
178
179
180
181
182

183
184

185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212



213

214
215
216
217
218
219
220
221
222
223
224
225
226
227
228






229
230
231
232
233
234
235
236
237
238
239
240
			# If there is no stepover active:
			if { $debug::stepOverLevel == -1} {
				set debug::stepOverLevel $debug::infoFrame
				break;
			}
		   }
		   breakpoints {
			set number [gets $debug::socketFd]

			set count 1
			#puts "Breakpoints: $number"

			while { $count <= $number } {
			#	puts ">>> $count"
				set breakpointFileName [gets $debug::socketFd]
				set breakpointFileLines [gets $debug::socketFd]
				set ::debug::breakpoints($breakpointFileName) [lsort -integer $breakpointFileLines]
			#	puts ">>> set $breakpointFileName:($breakpointFileLines)"
				incr count
			}
			set debug::breakpointsStatus 1
		   }
		   continue {
			# Run till next breakpoint
			set debug::breakpointsStatus 1
			# Do next step
			break
		   }
		   exit {
			close $debug::socketFd
			exit
		   }
		   default {
			puts $debug::socketFd "Invalid command."
			flush $debug::socketFd
		   }
   		}
	}
  }







  proc main {script} {
	  source $script
  }


if { $argc != 3 } {
	puts ">>> Please use following syntax:"
	puts ">>> ./debugServer.tcl tclFile currentDirectory portNumber"
	exit
}

set tclScript [lindex $::argv 0]
set currentDirectory [lindex $::argv 1]
set portNumber [lindex $::argv 2]







cd $currentDirectory
socket -server serverProc $portNumber
trace add execution ::debug::main [list enterstep] ::debug::step

::debug::main $tclScript

}











|
>
|

>
|







<



















>
>
>
|
>





<
|

|






>
>
>
>
>
>












182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201

202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230

231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
			# If there is no stepover active:
			if { $debug::stepOverLevel == -1} {
				set debug::stepOverLevel $debug::infoFrame
				break;
			}
		   }
		   breakpoints {
			set debug::breakpointsStatus [gets $debug::socketFd]
			unset -nocomplain ::debug::breakpoints

			#puts "Breakpoints: $number"
			set count 1
			while { $count <= $debug::breakpointsStatus } {
			#	puts ">>> $count"
				set breakpointFileName [gets $debug::socketFd]
				set breakpointFileLines [gets $debug::socketFd]
				set ::debug::breakpoints($breakpointFileName) [lsort -integer $breakpointFileLines]
			#	puts ">>> set $breakpointFileName:($breakpointFileLines)"
				incr count
			}

		   }
		   continue {
			# Run till next breakpoint
			set debug::breakpointsStatus 1
			# Do next step
			break
		   }
		   exit {
			close $debug::socketFd
			exit
		   }
		   default {
			puts $debug::socketFd "Invalid command."
			flush $debug::socketFd
		   }
   		}
	}
  }

  proc putsv args {
	if { $debug::isVerbose } {
   		puts stdout [lindex $args 0]
	}
  }

  proc main {script} {
	  source $script
  }


if { $argc <= 2 } {
	puts ">>> Please use following syntax:"
	puts ">>> ./debugServer.tcl tclFile currentDirectory portNumber optionalVerbose(0or1)"
	exit
}

set tclScript [lindex $::argv 0]
set currentDirectory [lindex $::argv 1]
set portNumber [lindex $::argv 2]

if { $argc == 4 } {
	set isVerbose [lindex $::argv 3]
} else {
	set isVerbose 0
}

cd $currentDirectory
socket -server serverProc $portNumber
trace add execution ::debug::main [list enterstep] ::debug::step

::debug::main $tclScript

}