Raw File
(**************************************************************************)
(*                                                                        *)
(*  This file is part of the Concurrency Plugin for Frama-C               *)
(*                                                                        *)
(*  Copyright (C) 2013                                                    *)
(*    Adelard LLP                                                         *)
(*    CINIF (Control and Instrumentation Nuclear Industry Forum)          *)
(*                                                                        *)
(*  You can redistribute it and/or modify it under the terms of the GNU   *)
(*  Lesser General Public License as published by the Free Software       *)
(*  Foundation, version 3.                                                *)
(*                                                                        *)
(*  It is distributed in the hope that it will be useful,                 *)
(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
(*  GNU Lesser General Public License for more details.                   *)
(*                                                                        *)
(*  See the GNU Lesser General Public License version v3                  *)
(*  for more details (enclosed in the file ./LGPLv3).                     *)
(*                                                                        *)
(**************************************************************************)

(**
* This is the main entry for the Concurrency plugin gui.
*)

(*GUIs*)
open Gtk_helper
open Pretty_source 

(*Plugin modules*)
open Concurrency_main
open Cil_datatype 
open Cil_types
open Visitor

(*open Option - was not been able to link this library ExtLib*)
open Concurguiglobs
open Vappgui
open Accessesgui 
open Epsgui


let show_column = ref (fun _ -> ());;	(*the 'Concurrency' column in filetree*)

(** Computes an intersection of the variable sets used by the main thread
 *  and the selected thread*)
let update_var_overlap () =
  if (List.length (SelectedThreads.get ())) > 0  && ((SelectedMain.get) () != init_fundec)
  then
    (
      let main_vars = variables_used (get_option !cg) (SelectedMain.get ()) in
 
      let intersect_vars st f2 =
	if f2 != (SelectedMain.get ()) then
	  Varinfo.Set.inter st (variables_used (get_option !cg) f2)
	else
	  st
      in
 
      let out_set = List.fold_left intersect_vars main_vars (SelectedThreads.get ()) in
	
      VarOverlapSet_ref.set out_set;
    )
  else
    () ;;
 
(** Updates the R/W access locations for a particular variable and thread 
 * and converts this data to the local data holder type*)
let update_acc() =
  if (List.length (SelectedThreads.get ())) > 0  
    && (SelectedMain.get ()) != init_fundec 
      && (LockedVar.get ()) != init_varinfo
  then
    begin
      (*obtains the locs for the current entry point*)
      let get_locs_for_v_from_ep v ep = 
	let vis = new varAccess v in
	let visfun _ f = 
	  let _ = visitFramacFunction (vis:>frama_c_visitor) f in ()
	in
	cg_dfs_fundecs (get_option !cg) visfun ep;
	vis#locs
      in
      
      (*converts locs to the AccessForList type*)
      let to_access_for ep (a, b, c) =
	(ep, a, b, c) 
      in
      
      let main_locs = get_locs_for_v_from_ep (LockedVar.get ()) (SelectedMain.get ()) in
      
      AccessForList.set [];
      AccessForList.set (List.append (AccessForList.get ()) (List.map (to_access_for (SelectedMain.get ())) main_locs));
      
      
      let apply_to_all_eps ep =
	let ep_locs = get_locs_for_v_from_ep (LockedVar.get ()) ep in
	AccessForList.set (List.append (AccessForList.get ()) (List.map (to_access_for ep) ep_locs))
      in
      List.iter apply_to_all_eps (SelectedThreads.get ())
    end ;;

(** Updates the pretty source viewer part of the Frama-C GUI
* to highlight the critical regions in the code.
*)
let concur_highlighter buffer loc ~start ~stop =
  if (List.length (AccessForList.get ())) > 0 then
    begin
      let highlight clr =
	(*make_tag apply_tag are frama-c functions*)
	let tag = make_tag buffer "concurrency" [`BACKGROUND clr ] in
        apply_tag buffer tag start stop
      in 
      
      match loc with
        | PLval (_, ki, (lh,_)) ->     
	  let same_lval ki lh (_,k,_,_) = 
	    (Kinstr.equal ki k) && 
	      match lh with 
		| Var(vi) -> (Varinfo.equal vi (LockedVar.get ()))  
		| _ ->  false 
	  in
	  if List.exists (same_lval ki lh) (AccessForList.get ()) then
 	    highlight "yellow";
	| PTermLval (_,ki,(lh,_)) ->	  
	  let same_tlval ki lh (_,k,_,_) = 
	    (Kinstr.equal ki k) && 
	      match lh with 
		| TVar(tv) ->
		  begin
		    match tv.lv_origin with
		      | Some(vi) -> (Varinfo.equal vi (LockedVar.get ()))  
		      | None ->  false
		  end
		| _ -> false
	  in 
	  if List.exists (same_tlval ki lh) (AccessForList.get ()) then 
	    highlight "yellow"
		
	| PVDecl(_, vi') when Varinfo.equal (LockedVar.get ()) vi' ->
	  highlight "yellow"
	    
        | PVDecl _ | PStmt _ | PGlobal _ | PIP _ -> ()
    end;;

(** Configures the concurrency plugin side panel and loads the additional forms. 
*)
let concurrency_panel main_ui =  
  let eps_display = new vEpsWindow in 			(*entry points form*)
  let var_overlap_display = new vAppWindow in		(*intersected variables form*)
  let access_display = new vAccWindow main_ui in	(*access form*) 

  (*setup the side panel*)
  let v_box = GPack.vbox () in 
  let h0_box = GPack.hbox ~packing: v_box#pack () in
  let _ = GMisc.label 
    ~xalign:0.0 
    ~text:"Filter:" 
    ~width:100
    ~packing:(h0_box#pack ~expand:false) () 
  in
  let open_ep_filter = GButton.button ~label:"Entry Points" ~packing:h0_box#pack () in
 
  ignore (open_ep_filter#connect#pressed (fun () -> eps_display#show_eps ()));

  (*Create Main combo and label*)
  let h2_box = GPack.hbox ~packing: v_box#pack () in

  let _ = GMisc.label 
    ~xalign:0.0 
    ~text:"Current main: "
    ~width:100
    ~packing:(h2_box#pack ~expand:false) () 
  in

  let main_combo = GEdit.combo_box_text ~strings:[] 
    ~has_frame:true ~packing:h2_box#pack () 
  in
  
  (*Populates the main entry point combo box with 
    selection of available entry points*)
  let populate_main () =
    let add_eps_to_combo ep = 
      let ep_name = (Kernel_function.get_name ep) in
      GEdit.text_combo_add main_combo ep_name
    in
    let (_,(a,_)) = main_combo in
    a#clear ();
    List.iter add_eps_to_combo (FilteredEntryPoints.get ()) 
  in

  let (a,_) = main_combo in
  ignore(a#connect#changed (fun () -> 
    let mn = match GEdit.text_combo_get_active main_combo with
      | Some(a) -> Globals.Functions.find_by_name a
      | None ->   init_fundec
    in
    if mn != init_fundec then
      begin
	SelectedMain.set mn; 
	LockedVar.set  init_varinfo; 
	AccessForList.set []; 
	SelectedThreads.set [];
	VarOverlapSet_ref.set Varinfo.Set.empty;
	refresh := (false,true,true,true,false)	(*update gui*)
      end));
 
  (*Create E.P. combo and label*)
  let h3_box = GPack.hbox ~packing: v_box#pack () in

  let _ = GMisc.label 
    ~xalign:0.0 
    ~text:"Current E.P.: " 
    ~width:100
    ~packing:(h3_box#pack ~expand:false) () 
  in

  let eps_combo = GEdit.combo_box_text ~strings:[] ~has_frame:true ~packing:h3_box#pack () in

  (* Populates the Entry Points combo with the 
  available entry points except what was selected as a main entry point*)
  let populate_eps () =
    let current_main = match GEdit.text_combo_get_active main_combo with
      | Some(a) -> a
      | None -> ""
    in
    let add_eps_to_combo ep = 
      let ep_name = (Kernel_function.get_name ep) in
      if not (ep_name = current_main) then
	GEdit.text_combo_add eps_combo ep_name
    in
    let (_,(a,_)) = eps_combo in
    a#clear ();
    List.iter add_eps_to_combo (FilteredEntryPoints.get ()) 
  in
  let (a,_) = eps_combo in
  ignore(a#connect#changed (
    fun () -> 
      let ep = match GEdit.text_combo_get_active eps_combo with
	| Some(a) -> Globals.Functions.find_by_name a
	| None ->  init_fundec
      in
      if ep != init_fundec then
	begin
	  SelectedThreads.set [ep];
	  LockedVar.set  init_varinfo; 
	  VarOverlapSet_ref.set Varinfo.Set.empty;
	  AccessForList.set [];
	  refresh := (false,false,true,true,false) (*update gui*)
	end));

  (*Create the currently selected variable combo box*)
  let h1_box = GPack.hbox ~packing: v_box#pack () in
  
  let _ = GMisc.label 
    ~xalign:0.0 
    ~text:"Current variable:" 
    ~width:100
    ~packing:(h1_box#pack ~expand:false) () 
  in
 
  let var_combo = GEdit.combo_box_text ~strings:[] ~has_frame:true ~packing:h1_box#pack () in

  (*Populates the combo box with the current 
  variables shared between main and EP thread.*)
  let populate_var () = 
    let (a,(b,_)) = var_combo in
    let add_vars_to_combo vi c =
      if not (Cil.isFunctionType vi.vtype) then begin
        GEdit.text_combo_add var_combo vi.vname;
      
        (*the purpose of this is to activate the currently selected variable*)
        if vi.vname == (LockedVar.get ()).vname then
	  a#set_active c; (*how to get the number of elements added*)
        c+1
      end
      else c
    in
    b#clear (); 
    ignore(Varinfo.Set.fold  add_vars_to_combo (VarOverlapSet_ref.get ()) 0);
  in

  let (a,_) = var_combo in
  ignore(a#connect#changed (fun () ->  
    let vi = match GEdit.text_combo_get_active var_combo with
      | Some(a) ->
        (try Globals.Vars.find_from_astinfo a VGlobal
         with Not_found -> init_varinfo)
      | None ->  init_varinfo
    in
    if vi != init_varinfo then
      LockedVar.set vi;
    AccessForList.set [];
    refresh := (false, false, false, true, false) (*update gui*)
  ));

  (*create buttons*)
  let show_vapp = GButton.button ~label:"Details" ~packing:h1_box#pack () in
 
  ignore (show_vapp#connect#pressed (fun () -> var_overlap_display#show_vapp ()));
  
  let h4_box = GPack.hbox ~packing: v_box#pack () in 
  let _ = GMisc.label 
    ~xalign:0.0 
    ~text:"Compute:" 
    ~width:100
    ~packing:(h4_box#pack ~expand:false) () 
  in

  let button_access_details = GButton.button ~label:"Show Details" ~packing:h4_box#pack () in
  ignore (button_access_details#connect#pressed(fun () -> update_acc (); access_display#show_acc ()));

  (* Drives the GUI statemachine updating the Concurrency section of the GUI*)
  let refresh = fun () -> 
    begin 
      (*We could replace this by some sort of signal*)
      let (m,e,v,g,_) = !refresh in
      if m then
	populate_main ();

      (*main has changed*)
      if e then
	populate_eps ();
      
      (*main or eps have changed*)
      if v then 
	begin
	  update_var_overlap (); 
	  populate_var ();
	  !show_column `Visibility
	end;

      (*main or eps or var have changed*)
      if g then
	begin
	  populate_var (); 
	  update_acc ();
	  !show_column `Visibility
	end;

     if m || e || v || g then
       (
	 var_overlap_display#refresh ();
	 access_display#refresh (); 
	 main_ui#rehighlight ();
       );

     
      refresh := (false,false, false, false, false)
    end	
  in
  "Concurrency", v_box#coerce, Some refresh 
;;     

(** Decorates the file tree Concurrency colum to show the 
 * functions where a particular variable is used.
*)
let file_tree_decorate (file_tree:Filetree.t) =
  show_column := 
    file_tree#append_pixbuf_column 
    ~title:"Concurrency"
    (fun globs ->
      if  List.length (AccessForList.get ()) > 0 then
	begin
          let in_globals (_,ki,_,_) =
            match ki with
              | Kglobal -> false
              | Kstmt stmt ->
		let kf = Kernel_function.find_englobing_kf stmt in
		let {vid=v0} = Kernel_function.get_vi kf in
		List.exists
                  (fun glob -> match glob with
                    | GFun ({svar ={vid=v1}},_ ) -> v1=v0
                    |  _ -> false)
                  globs
          in
          if (List.exists in_globals (AccessForList.get ())) then [`STOCK_ID "gtk-apply"]
          else [`STOCK_ID ""]
	end
      else
        [`STOCK_ID ""])
    (fun () -> not (Varinfo.equal (LockedVar.get ()) init_varinfo));
  !show_column `Visibility ;;


let concur_gui_f main_ui= 
  (*init*)
  start_concurrency ();
  Self.result "Starting concur plugin";
  (*menu accessible from right clicking on the source viewer*)
  (*main_ui#register_source_selector concur_menu_item;*)
  (*highlights in main code display when necessary parameters of the plugin 
  are set*)
  main_ui#register_source_highlighter concur_highlighter;
  main_ui#register_panel concurrency_panel;
  file_tree_decorate main_ui#file_tree;
;;

(*Register the main_gui function as enty point for Concur GUI*)
let () = Design.register_extension concur_gui_f ;;
back to top